diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-23 03:58:43 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-11-23 03:58:43 +0000 |
commit | 8d1f875d17695b508b8ac6ada9cef468f6fd181e (patch) | |
tree | 71dd26ccb18e60b4e569c738715a3153e5e91042 /remoting/protocol | |
parent | 313b80bd2c5b7257d8daa2ef4aef0ee5b6e1555c (diff) | |
download | chromium_src-8d1f875d17695b508b8ac6ada9cef468f6fd181e.zip chromium_src-8d1f875d17695b508b8ac6ada9cef468f6fd181e.tar.gz chromium_src-8d1f875d17695b508b8ac6ada9cef468f6fd181e.tar.bz2 |
Move SSL layer initialization into ChannelAuthenticator implementations.
Also separate client and host authenticators into separate files.
Review URL: http://codereview.chromium.org/8604001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@111311 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/protocol')
19 files changed, 537 insertions, 575 deletions
diff --git a/remoting/protocol/auth_token_utils.cc b/remoting/protocol/auth_util.cc index 50edca3..51f7af5 100644 --- a/remoting/protocol/auth_token_utils.cc +++ b/remoting/protocol/auth_util.cc @@ -2,16 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/protocol/auth_token_utils.h" +#include "remoting/protocol/auth_util.h" #include "base/base64.h" #include "base/logging.h" #include "base/string_util.h" +#include "crypto/hmac.h" #include "crypto/sha2.h" namespace remoting { namespace protocol { +const char kClientAuthSslExporterLabel[] = + "EXPORTER-remoting-channel-auth-client"; + +const char kSslFakeHostName[] = "chromoting"; + std::string GenerateSupportAuthToken(const std::string& jid, const std::string& access_code) { std::string sha256 = crypto::SHA256HashString(jid + " " + access_code); @@ -30,5 +36,25 @@ bool VerifySupportAuthToken(const std::string& jid, return expected_token == auth_token; } +// static +bool GetAuthBytes(const std::string& shared_secret, + const std::string& key_material, + std::string* auth_bytes) { + // Generate auth digest based on the keying material and shared secret. + crypto::HMAC response(crypto::HMAC::SHA256); + if (!response.Init(key_material)) { + NOTREACHED() << "HMAC::Init failed"; + return false; + } + unsigned char out_bytes[kAuthDigestLength]; + if (!response.Sign(shared_secret, out_bytes, kAuthDigestLength)) { + NOTREACHED() << "HMAC::Sign failed"; + return false; + } + + auth_bytes->assign(out_bytes, out_bytes + kAuthDigestLength); + return true; +} + } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/auth_token_utils.h b/remoting/protocol/auth_util.h index 9be1170..7a81c12 100644 --- a/remoting/protocol/auth_token_utils.h +++ b/remoting/protocol/auth_util.h @@ -2,14 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef REMOTING_PROTOCOL_AUTH_TOKEN_UTILS_H_ -#define REMOTING_PROTOCOL_AUTH_TOKEN_UTILS_H_ +#ifndef REMOTING_PROTOCOL_AUTH_UTIL_H_ +#define REMOTING_PROTOCOL_AUTH_UTIL_H_ #include <string> namespace remoting { namespace protocol { +// Labels for use when exporting the SSL master keys. +extern const char kClientAuthSslExporterLabel[]; + +// Fake hostname used for SSL connections. +extern const char kSslFakeHostName[]; + +// Size of the HMAC-SHA-256 authentication digest. +const size_t kAuthDigestLength = 32; + // Generates auth token for the specified |jid| and |access_code|. std::string GenerateSupportAuthToken(const std::string& jid, const std::string& access_code); @@ -19,7 +28,12 @@ bool VerifySupportAuthToken(const std::string& jid, const std::string& access_code, const std::string& auth_token); +// Returns hash used for channel authentication. +bool GetAuthBytes(const std::string& shared_secret, + const std::string& key_material, + std::string* auth_bytes); + } // namespace protocol } // namespace remoting -#endif // REMOTING_PROTOCOL_AUTH_TOKEN_UTILS_H_ +#endif // REMOTING_PROTOCOL_AUTH_UTIL_H_ diff --git a/remoting/protocol/channel_authenticator.cc b/remoting/protocol/channel_authenticator.cc deleted file mode 100644 index fcce9b1..0000000 --- a/remoting/protocol/channel_authenticator.cc +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) 2011 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 "remoting/protocol/channel_authenticator.h" - -#include "base/compiler_specific.h" -#include "base/string_piece.h" -#include "crypto/hmac.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/socket/ssl_socket.h" -#include "net/socket/stream_socket.h" - -namespace remoting { -namespace protocol { - -namespace { - -// Labels for use when exporting the SSL master keys. -const char kClientSslExporterLabel[] = "EXPORTER-remoting-channel-auth-client"; - -// Size of the HMAC-SHA-256 authentication digest. -const size_t kAuthDigestLength = 32; - -// static -bool GetAuthBytes(const std::string& shared_secret, - const std::string& key_material, - std::string* auth_bytes) { - // Generate auth digest based on the keying material and shared secret. - crypto::HMAC response(crypto::HMAC::SHA256); - if (!response.Init(key_material)) { - NOTREACHED() << "HMAC::Init failed"; - return false; - } - unsigned char out_bytes[kAuthDigestLength]; - if (!response.Sign(shared_secret, out_bytes, kAuthDigestLength)) { - NOTREACHED() << "HMAC::Sign failed"; - return false; - } - - auth_bytes->assign(out_bytes, out_bytes + kAuthDigestLength); - return true; -} - -} // namespace - -HostChannelAuthenticator::HostChannelAuthenticator( - const std::string& shared_secret) - : shared_secret_(shared_secret), - socket_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(auth_read_callback_( - this, &HostChannelAuthenticator::OnAuthBytesRead)) { -} - -HostChannelAuthenticator::~HostChannelAuthenticator() { -} - -void HostChannelAuthenticator::Authenticate(net::SSLSocket* socket, - const DoneCallback& done_callback) { - DCHECK(CalledOnValidThread()); - - socket_ = socket; - done_callback_ = done_callback; - - unsigned char key_material[kAuthDigestLength]; - int result = socket_->ExportKeyingMaterial( - kClientSslExporterLabel, "", key_material, kAuthDigestLength); - if (result != net::OK) { - LOG(ERROR) << "Error fetching keying material: " << result; - done_callback.Run(FAILURE); - return; - } - - if (!GetAuthBytes(shared_secret_, - std::string(key_material, key_material + kAuthDigestLength), - &auth_bytes_)) { - done_callback.Run(FAILURE); - return; - } - - // Read an authentication digest. - auth_read_buf_ = new net::GrowableIOBuffer(); - auth_read_buf_->SetCapacity(kAuthDigestLength); - DoAuthRead(); -} - -void HostChannelAuthenticator::DoAuthRead() { - while (true) { - int result = socket_->Read(auth_read_buf_, - auth_read_buf_->RemainingCapacity(), - &auth_read_callback_); - if (result == net::ERR_IO_PENDING) - break; - if (!HandleAuthBytesRead(result)) - break; - } -} - -void HostChannelAuthenticator::OnAuthBytesRead(int result) { - DCHECK(CalledOnValidThread()); - - if (HandleAuthBytesRead(result)) - DoAuthRead(); -} - -bool HostChannelAuthenticator::HandleAuthBytesRead(int read_result) { - if (read_result <= 0) { - LOG(ERROR) << "Error reading authentication: " << read_result; - done_callback_.Run(FAILURE); - return false; - } - - auth_read_buf_->set_offset(auth_read_buf_->offset() + read_result); - if (auth_read_buf_->RemainingCapacity() > 0) - return true; - - if (!VerifyAuthBytes(std::string( - auth_read_buf_->StartOfBuffer(), - auth_read_buf_->StartOfBuffer() + kAuthDigestLength))) { - LOG(ERROR) << "Mismatched authentication"; - done_callback_.Run(FAILURE); - return false; - } - - done_callback_.Run(SUCCESS); - return false; -} - -bool HostChannelAuthenticator::VerifyAuthBytes( - const std::string& received_auth_bytes) { - DCHECK(received_auth_bytes.length() == kAuthDigestLength); - - // Compare the received and expected digests in fixed time, to limit the - // scope for timing attacks. - uint8 result = 0; - for (unsigned i = 0; i < auth_bytes_.length(); i++) { - result |= received_auth_bytes[i] ^ auth_bytes_[i]; - } - return result == 0; -} - -ClientChannelAuthenticator::ClientChannelAuthenticator( - const std::string& shared_secret) - : shared_secret_(shared_secret), -socket_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(auth_write_callback_( - this, &ClientChannelAuthenticator::OnAuthBytesWritten)) { -} - -ClientChannelAuthenticator::~ClientChannelAuthenticator() { -} - -void ClientChannelAuthenticator::Authenticate( - net::SSLSocket* socket, - const DoneCallback& done_callback) { - DCHECK(CalledOnValidThread()); - - socket_ = socket; - done_callback_ = done_callback; - - unsigned char key_material[kAuthDigestLength]; - int result = socket_->ExportKeyingMaterial( - kClientSslExporterLabel, "", key_material, kAuthDigestLength); - if (result != net::OK) { - LOG(ERROR) << "Error fetching keying material: " << result; - done_callback.Run(FAILURE); - return; - } - - std::string auth_bytes; - if (!GetAuthBytes(shared_secret_, - std::string(key_material, key_material + kAuthDigestLength), - &auth_bytes)) { - done_callback.Run(FAILURE); - return; - } - - // Allocate a buffer to write the authentication digest. - auth_write_buf_ = new net::DrainableIOBuffer( - new net::StringIOBuffer(auth_bytes), auth_bytes.size()); - DoAuthWrite(); -} - -void ClientChannelAuthenticator::DoAuthWrite() { - while (true) { - int result = socket_->Write(auth_write_buf_, - auth_write_buf_->BytesRemaining(), - &auth_write_callback_); - if (result == net::ERR_IO_PENDING) - break; - if (!HandleAuthBytesWritten(result)) - break; - } -} - -void ClientChannelAuthenticator::OnAuthBytesWritten(int result) { - DCHECK(CalledOnValidThread()); - - if (HandleAuthBytesWritten(result)) - DoAuthWrite(); -} - -bool ClientChannelAuthenticator::HandleAuthBytesWritten(int result) { - if (result <= 0) { - LOG(ERROR) << "Error writing authentication: " << result; - done_callback_.Run(FAILURE); - return false; - } - - auth_write_buf_->DidConsume(result); - if (auth_write_buf_->BytesRemaining() > 0) - return true; - - done_callback_.Run(SUCCESS); - return false; -} - -} // namespace protocol -} // namespace remoting diff --git a/remoting/protocol/channel_authenticator.h b/remoting/protocol/channel_authenticator.h index c925ea9..535730a 100644 --- a/remoting/protocol/channel_authenticator.h +++ b/remoting/protocol/channel_authenticator.h @@ -8,93 +8,33 @@ #include <string> #include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/threading/non_thread_safe.h" -#include "net/base/completion_callback.h" +#include "net/base/net_errors.h" namespace net { -class DrainableIOBuffer; -class GrowableIOBuffer; -class SSLSocket; +class StreamSocket; } // namespace net namespace remoting { namespace protocol { -class ChannelAuthenticator : public base::NonThreadSafe { +// Interface for channel authentications that perform channel-level +// authentication. Depending on implementation channel authenticators +// may also establish SSL connection. Each instance of this interface +// should be used only once for one channel. +class ChannelAuthenticator { public: - enum Result { - SUCCESS, - FAILURE, - }; - - typedef base::Callback<void(Result)> DoneCallback; - - ChannelAuthenticator() { } - virtual ~ChannelAuthenticator() { } - - // Starts authentication of the |socket|. |done_callback| is called - // when authentication is finished. Caller retains ownership of - // |socket|. |shared_secret| is a shared secret that we use to - // authenticate the channel. - virtual void Authenticate(net::SSLSocket* socket, - const DoneCallback& done_callback) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(ChannelAuthenticator); -}; - -class HostChannelAuthenticator : public ChannelAuthenticator { - public: - HostChannelAuthenticator(const std::string& shared_secret); - virtual ~HostChannelAuthenticator(); - - // ChannelAuthenticator overrides. - virtual void Authenticate(net::SSLSocket* socket, - const DoneCallback& done_callback) OVERRIDE; - - private: - void DoAuthRead(); - void OnAuthBytesRead(int result); - bool HandleAuthBytesRead(int result); - bool VerifyAuthBytes(const std::string& received_auth_bytes); - - std::string shared_secret_; - std::string auth_bytes_; - net::SSLSocket* socket_; - DoneCallback done_callback_; - - scoped_refptr<net::GrowableIOBuffer> auth_read_buf_; - - net::OldCompletionCallbackImpl<HostChannelAuthenticator> auth_read_callback_; - - DISALLOW_COPY_AND_ASSIGN(HostChannelAuthenticator); -}; - -class ClientChannelAuthenticator : public ChannelAuthenticator { - public: - ClientChannelAuthenticator(const std::string& shared_secret); - virtual ~ClientChannelAuthenticator(); - - // ChannelAuthenticator overrides. - virtual void Authenticate(net::SSLSocket* socket, - const DoneCallback& done_callback) OVERRIDE; - - private: - void DoAuthWrite(); - void OnAuthBytesWritten(int result); - bool HandleAuthBytesWritten(int result); - - std::string shared_secret_; - net::SSLSocket* socket_; - DoneCallback done_callback_; - - scoped_refptr<net::DrainableIOBuffer> auth_write_buf_; - - net::OldCompletionCallbackImpl<ClientChannelAuthenticator> - auth_write_callback_; - - DISALLOW_COPY_AND_ASSIGN(ClientChannelAuthenticator); + typedef base::Callback<void(net::Error error, net::StreamSocket*)> + DoneCallback; + + virtual ~ChannelAuthenticator() {} + + // Start authentication of the given |socket|. Takes ownership of + // |socket|, and caller must not use |socket| after calling this + // method. |done_callback| is called when authentication is + // finished. Callback may be invoked before this method + // returns. Callback handler must take ownership of the result. + virtual void SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) = 0; }; } // namespace protocol diff --git a/remoting/protocol/connection_to_host.cc b/remoting/protocol/connection_to_host.cc index f3638ad..53bc030 100644 --- a/remoting/protocol/connection_to_host.cc +++ b/remoting/protocol/connection_to_host.cc @@ -11,7 +11,7 @@ #include "remoting/base/constants.h" #include "remoting/jingle_glue/javascript_signal_strategy.h" #include "remoting/jingle_glue/xmpp_signal_strategy.h" -#include "remoting/protocol/auth_token_utils.h" +#include "remoting/protocol/auth_util.h" #include "remoting/protocol/client_control_dispatcher.h" #include "remoting/protocol/client_event_dispatcher.h" #include "remoting/protocol/client_stub.h" diff --git a/remoting/protocol/jingle_channel_connector.h b/remoting/protocol/jingle_channel_connector.h index 8327ebf..09c8150 100644 --- a/remoting/protocol/jingle_channel_connector.h +++ b/remoting/protocol/jingle_channel_connector.h @@ -21,15 +21,16 @@ class RSAPrivateKey; namespace remoting { namespace protocol { +class ChannelAuthenticator; + class JingleChannelConnector : public base::NonThreadSafe { public: JingleChannelConnector() { } virtual ~JingleChannelConnector() { } - virtual void Connect(bool initiator, - const std::string& local_cert, - const std::string& remote_cert, - crypto::RSAPrivateKey* local_private_key, + // Starts the connection process for the channel. Takes ownership of + // |authenticator|. + virtual void Connect(ChannelAuthenticator* authenticator, cricket::TransportChannel* raw_channel) = 0; protected: diff --git a/remoting/protocol/jingle_datagram_connector.cc b/remoting/protocol/jingle_datagram_connector.cc index 4a90b3d..4673efc 100644 --- a/remoting/protocol/jingle_datagram_connector.cc +++ b/remoting/protocol/jingle_datagram_connector.cc @@ -5,6 +5,7 @@ #include "remoting/protocol/jingle_datagram_connector.h" #include "jingle/glue/channel_socket_adapter.h" +#include "remoting/protocol/channel_authenticator.h" #include "remoting/protocol/jingle_session.h" namespace remoting { @@ -23,13 +24,12 @@ JingleDatagramConnector::~JingleDatagramConnector() { } void JingleDatagramConnector::Connect( - bool initiator, - const std::string& local_cert, - const std::string& remote_cert, - crypto::RSAPrivateKey* local_private_key, + ChannelAuthenticator* authenticator, cricket::TransportChannel* raw_channel) { DCHECK(CalledOnValidThread()); + authenticator_.reset(authenticator); + net::Socket* socket = new jingle_glue::TransportChannelSocketAdapter(raw_channel); diff --git a/remoting/protocol/jingle_datagram_connector.h b/remoting/protocol/jingle_datagram_connector.h index 621bbee..930ec0f 100644 --- a/remoting/protocol/jingle_datagram_connector.h +++ b/remoting/protocol/jingle_datagram_connector.h @@ -23,13 +23,11 @@ class JingleDatagramConnector : public JingleChannelConnector { const Session::DatagramChannelCallback& callback); virtual ~JingleDatagramConnector(); - // Starts connection process for the channel. |local_private_key| is - // owned by the caller, and must exist until this object is - // destroyed. - virtual void Connect(bool initiator, - const std::string& local_cert, - const std::string& remote_cert, - crypto::RSAPrivateKey* local_private_key, + // JingleChannelConnector implementation. + // TODO(sergeyu): In the current implementation ChannelAuthenticator + // cannot be used for datagram channels, so needs to be either + // extended or replaced with something else here. + virtual void Connect(ChannelAuthenticator* authenticator, cricket::TransportChannel* raw_channel) OVERRIDE; private: @@ -37,6 +35,8 @@ class JingleDatagramConnector : public JingleChannelConnector { std::string name_; Session::DatagramChannelCallback callback_; + scoped_ptr<ChannelAuthenticator> authenticator_; + DISALLOW_COPY_AND_ASSIGN(JingleDatagramConnector); }; diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc index 8e22f54..21f8426 100644 --- a/remoting/protocol/jingle_session.cc +++ b/remoting/protocol/jingle_session.cc @@ -17,6 +17,8 @@ #include "remoting/protocol/jingle_datagram_connector.h" #include "remoting/protocol/jingle_session_manager.h" #include "remoting/protocol/jingle_stream_connector.h" +#include "remoting/protocol/v1_client_channel_authenticator.h" +#include "remoting/protocol/v1_host_channel_authenticator.h" #include "third_party/libjingle/source/talk/base/thread.h" #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" #include "third_party/libjingle/source/talk/p2p/base/session.h" @@ -413,8 +415,15 @@ void JingleSession::AddChannelConnector( } channel_connectors_[name] = connector; - connector->Connect(cricket_session_->initiator(), local_cert_, - remote_cert_, local_private_key_.get(), raw_channel); + ChannelAuthenticator* authenticator; + if (cricket_session_->initiator()) { + authenticator = new V1ClientChannelAuthenticator( + remote_cert_, shared_secret_); + } else { + authenticator = new V1HostChannelAuthenticator( + local_cert_, local_private_key_.get(), shared_secret_); + } + connector->Connect(authenticator, raw_channel); // Workaround bug in libjingle - it doesn't connect channels if they // are created after the session is accepted. See crbug.com/89384. diff --git a/remoting/protocol/jingle_stream_connector.cc b/remoting/protocol/jingle_stream_connector.cc index 44cc3c5..4eab85e 100644 --- a/remoting/protocol/jingle_stream_connector.cc +++ b/remoting/protocol/jingle_stream_connector.cc @@ -32,34 +32,6 @@ const int kTcpAckDelayMilliseconds = 10; const int kTcpReceiveBufferSize = 256 * 1024; const int kTcpSendBufferSize = kTcpReceiveBufferSize + 30 * 1024; -// Helper method to create a SSL client socket. -net::SSLClientSocket* CreateSSLClientSocket( - net::StreamSocket* socket, const std::string& der_cert, - net::CertVerifier* cert_verifier) { - net::SSLConfig ssl_config; - - // Certificate provided by the host doesn't need authority. - net::SSLConfig::CertAndStatus cert_and_status; - cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; - cert_and_status.der_cert = der_cert; - ssl_config.allowed_bad_certs.push_back(cert_and_status); - - // Revocation checking is not needed because we use self-signed - // certs. Disable it so that SSL layer doesn't try to initialize - // OCSP (OCSP works only on IO thread). - ssl_config.rev_checking_enabled = false; - - // SSLClientSocket takes ownership of the adapter. - net::HostPortPair host_and_port( - ContentDescription::kChromotingContentName, 0); - net::SSLClientSocketContext context; - context.cert_verifier = cert_verifier; - net::SSLClientSocket* ssl_socket = - net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( - socket, host_and_port, ssl_config, NULL, context); - return ssl_socket; -} - } // namespace JingleStreamConnector::JingleStreamConnector( @@ -69,30 +41,20 @@ JingleStreamConnector::JingleStreamConnector( : session_(session), name_(name), callback_(callback), - initiator_(false), - local_private_key_(NULL), raw_channel_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(tcp_connect_callback_( - this, &JingleStreamConnector::OnTCPConnect)), - ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( - this, &JingleStreamConnector::OnSSLConnect)) { + this, &JingleStreamConnector::OnTCPConnect)) { } JingleStreamConnector::~JingleStreamConnector() { } -void JingleStreamConnector::Connect(bool initiator, - const std::string& local_cert, - const std::string& remote_cert, - crypto::RSAPrivateKey* local_private_key, +void JingleStreamConnector::Connect(ChannelAuthenticator* authenticator, cricket::TransportChannel* raw_channel) { DCHECK(CalledOnValidThread()); DCHECK(!raw_channel_); - initiator_ = initiator; - local_cert_ = local_cert; - remote_cert_ = remote_cert; - local_private_key_ = local_private_key; + authenticator_.reset(authenticator); raw_channel_ = raw_channel; net::Socket* socket = @@ -132,49 +94,6 @@ bool JingleStreamConnector::EstablishTCPConnection(net::Socket* socket) { return false; } -bool JingleStreamConnector::EstablishSSLConnection() { - DCHECK(tcp_socket_->IsConnected()); - - int result; - if (initiator_) { - cert_verifier_.reset(new net::CertVerifier()); - - // Create client SSL socket. - net::SSLClientSocket* socket = CreateSSLClientSocket( - tcp_socket_.release(), remote_cert_, cert_verifier_.get()); - socket_.reset(socket); - - result = socket->Connect(&ssl_connect_callback_); - } else { - scoped_refptr<net::X509Certificate> cert = - net::X509Certificate::CreateFromBytes( - local_cert_.data(), local_cert_.length()); - if (!cert) { - LOG(ERROR) << "Failed to parse X509Certificate"; - return false; - } - - // Create server SSL socket. - net::SSLConfig ssl_config; - net::SSLServerSocket* socket = net::CreateSSLServerSocket( - tcp_socket_.release(), cert, local_private_key_, ssl_config); - socket_.reset(socket); - - result = socket->Handshake(&ssl_connect_callback_); - } - - if (result == net::ERR_IO_PENDING) { - return true; - } else if (result != net::OK) { - LOG(ERROR) << "Failed to establish SSL connection"; - return false; - } - - // Reach here if net::OK is received. - ssl_connect_callback_.Run(net::OK); - return true; -} - void JingleStreamConnector::OnTCPConnect(int result) { DCHECK(CalledOnValidThread()); @@ -184,45 +103,16 @@ void JingleStreamConnector::OnTCPConnect(int result) { return; } - if (!EstablishSSLConnection()) - NotifyError(); -} - -void JingleStreamConnector::OnSSLConnect(int result) { - DCHECK(CalledOnValidThread()); - - if (result != net::OK) { - LOG(ERROR) << "Error during SSL connection: " << result; - NotifyError(); - return; - } - - DCHECK(socket_->IsConnected()); - AuthenticateChannel(); -} - -void JingleStreamConnector::AuthenticateChannel() { - if (initiator_) { - authenticator_.reset( - new ClientChannelAuthenticator(session_->shared_secret())); - } else { - authenticator_.reset( - new HostChannelAuthenticator(session_->shared_secret())); - } - authenticator_->Authenticate(socket_.get(), base::Bind( + authenticator_->SecureAndAuthenticate(tcp_socket_.release(), base::Bind( &JingleStreamConnector::OnAuthenticationDone, base::Unretained(this))); } void JingleStreamConnector::OnAuthenticationDone( - ChannelAuthenticator::Result result) { - switch (result) { - case ChannelAuthenticator::SUCCESS: - NotifyDone(socket_.release()); - break; - - case ChannelAuthenticator::FAILURE: - NotifyError(); - break; + net::Error error, net::StreamSocket* socket) { + if (error != net::OK) { + NotifyError(); + } else { + NotifyDone(socket); } } @@ -233,7 +123,6 @@ void JingleStreamConnector::NotifyDone(net::StreamSocket* socket) { } void JingleStreamConnector::NotifyError() { - socket_.reset(); NotifyDone(NULL); } diff --git a/remoting/protocol/jingle_stream_connector.h b/remoting/protocol/jingle_stream_connector.h index 27103de..f37a691 100644 --- a/remoting/protocol/jingle_stream_connector.h +++ b/remoting/protocol/jingle_stream_connector.h @@ -41,24 +41,14 @@ class JingleStreamConnector : public JingleChannelConnector { const Session::StreamChannelCallback& callback); virtual ~JingleStreamConnector(); - // Starts connection process for the channel. |local_private_key| is - // owned by the caller, and must exist until this object is - // destroyed. - virtual void Connect(bool initiator, - const std::string& local_cert, - const std::string& remote_cert, - crypto::RSAPrivateKey* local_private_key, + // JingleChannelConnector implementation. + virtual void Connect(ChannelAuthenticator* authenticator, cricket::TransportChannel* raw_channel) OVERRIDE; private: bool EstablishTCPConnection(net::Socket* socket); void OnTCPConnect(int result); - - bool EstablishSSLConnection(); - void OnSSLConnect(int result); - - void AuthenticateChannel(); - void OnAuthenticationDone(ChannelAuthenticator::Result result); + void OnAuthenticationDone(net::Error error, net::StreamSocket* socket); void NotifyDone(net::StreamSocket* socket); void NotifyError(); @@ -67,23 +57,14 @@ class JingleStreamConnector : public JingleChannelConnector { std::string name_; Session::StreamChannelCallback callback_; - bool initiator_; - std::string local_cert_; - std::string remote_cert_; - crypto::RSAPrivateKey* local_private_key_; - cricket::TransportChannel* raw_channel_; scoped_ptr<net::StreamSocket> tcp_socket_; scoped_ptr<net::SSLSocket> socket_; - // Used to verify the certificate received in SSLClientSocket. - scoped_ptr<net::CertVerifier> cert_verifier_; - scoped_ptr<ChannelAuthenticator> authenticator_; // Callback called by the TCP and SSL layers. net::OldCompletionCallbackImpl<JingleStreamConnector> tcp_connect_callback_; - net::OldCompletionCallbackImpl<JingleStreamConnector> ssl_connect_callback_; DISALLOW_COPY_AND_ASSIGN(JingleStreamConnector); }; diff --git a/remoting/protocol/pepper_channel.h b/remoting/protocol/pepper_channel.h index cb77b17..de6b93b 100644 --- a/remoting/protocol/pepper_channel.h +++ b/remoting/protocol/pepper_channel.h @@ -21,6 +21,7 @@ class Candidate; namespace remoting { namespace protocol { +class ChannelAuthenticator; struct TransportConfig; // Interface for stream and datagram channels used by PepperSession. @@ -29,10 +30,12 @@ class PepperChannel : public base::NonThreadSafe { PepperChannel() { } virtual ~PepperChannel() { } - // Connect the channel using specified |config|. + // Connect the channel using specified |config|. The specified + // |authenticator| is used to authenticate the channel. Takes + // ownership of |authenticator|. virtual void Connect(pp::Instance* pp_instance, const TransportConfig& config, - const std::string& remote_cert) = 0; + ChannelAuthenticator* authenticator) = 0; // Adds |candidate| received from the peer. virtual void AddRemoveCandidate(const cricket::Candidate& candidate) = 0; @@ -40,7 +43,7 @@ class PepperChannel : public base::NonThreadSafe { // Name of the channel. virtual const std::string& name() const = 0; - // returns true if the channel is already connected + // Returns true if the channel is already connected. virtual bool is_connected() const = 0; protected: diff --git a/remoting/protocol/pepper_session.cc b/remoting/protocol/pepper_session.cc index d9bab52..138993b 100644 --- a/remoting/protocol/pepper_session.cc +++ b/remoting/protocol/pepper_session.cc @@ -14,6 +14,7 @@ #include "remoting/protocol/jingle_messages.h" #include "remoting/protocol/pepper_session_manager.h" #include "remoting/protocol/pepper_stream_channel.h" +#include "remoting/protocol/v1_client_channel_authenticator.h" #include "third_party/libjingle/source/talk/p2p/base/candidate.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" @@ -114,7 +115,9 @@ void PepperSession::CreateStreamChannel( PepperStreamChannel* channel = new PepperStreamChannel(this, name, callback); channels_[name] = channel; channel->Connect(session_manager_->pp_instance_, - session_manager_->transport_config_, remote_cert_); + session_manager_->transport_config_, + new V1ClientChannelAuthenticator( + remote_cert_, shared_secret_)); } void PepperSession::CreateDatagramChannel( diff --git a/remoting/protocol/pepper_stream_channel.cc b/remoting/protocol/pepper_stream_channel.cc index 84abe25..e51d265 100644 --- a/remoting/protocol/pepper_stream_channel.cc +++ b/remoting/protocol/pepper_stream_channel.cc @@ -35,33 +35,6 @@ const int kTcpAckDelayMilliseconds = 10; const int kTcpReceiveBufferSize = 256 * 1024; const int kTcpSendBufferSize = kTcpReceiveBufferSize + 30 * 1024; -// Helper method to create a SSL client socket. -net::SSLClientSocket* CreateSSLClientSocket( - net::StreamSocket* socket, const std::string& der_cert, - net::CertVerifier* cert_verifier) { - net::SSLConfig ssl_config; - - // Certificate provided by the host doesn't need authority. - net::SSLConfig::CertAndStatus cert_and_status; - cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; - cert_and_status.der_cert = der_cert; - ssl_config.allowed_bad_certs.push_back(cert_and_status); - - // Revocation checking is not needed because we use self-signed - // certs. Disable it so that SSL layer doesn't try to initialize - // OCSP (OCSP works only on IO thread). - ssl_config.rev_checking_enabled = false; - - // SSLClientSocket takes ownership of the |socket|. - net::HostPortPair host_and_port("chromoting", 0); - net::SSLClientSocketContext context; - context.cert_verifier = cert_verifier; - net::SSLClientSocket* ssl_socket = - net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( - socket, host_and_port, ssl_config, NULL, context); - return ssl_socket; -} - } // namespace PepperStreamChannel::PepperStreamChannel( @@ -73,11 +46,8 @@ PepperStreamChannel::PepperStreamChannel( callback_(callback), channel_(NULL), connected_(false), - ssl_client_socket_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(p2p_connect_callback_( - this, &PepperStreamChannel::OnP2PConnect)), - ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( - this, &PepperStreamChannel::OnSSLConnect)) { + this, &PepperStreamChannel::OnP2PConnect)) { } PepperStreamChannel::~PepperStreamChannel() { @@ -90,10 +60,10 @@ PepperStreamChannel::~PepperStreamChannel() { void PepperStreamChannel::Connect(pp::Instance* pp_instance, const TransportConfig& transport_config, - const std::string& remote_cert) { + ChannelAuthenticator* authenticator) { DCHECK(CalledOnValidThread()); - remote_cert_ = remote_cert; + authenticator_.reset(authenticator); pp::Transport_Dev* transport = new pp::Transport_Dev(pp_instance, name_.c_str(), @@ -197,69 +167,23 @@ void PepperStreamChannel::OnChannelNewLocalCandidate( void PepperStreamChannel::OnP2PConnect(int result) { DCHECK(CalledOnValidThread()); - if (result != net::OK || !EstablishSSLConnection()) + if (result != net::OK) NotifyConnectFailed(); -} - -bool PepperStreamChannel::EstablishSSLConnection() { - DCHECK(CalledOnValidThread()); - - cert_verifier_.reset(new net::CertVerifier()); - - // Create client SSL socket. - ssl_client_socket_ = CreateSSLClientSocket( - owned_channel_.release(), remote_cert_, cert_verifier_.get()); - socket_.reset(ssl_client_socket_); - int result = ssl_client_socket_->Connect(&ssl_connect_callback_); - - if (result == net::ERR_IO_PENDING) { - return true; - } else if (result != net::OK) { - LOG(ERROR) << "Failed to establish SSL connection"; - return false; - } - - // Reach here if net::OK is received. - ssl_connect_callback_.Run(net::OK); - return true; + authenticator_->SecureAndAuthenticate(owned_channel_.release(), base::Bind( + &PepperStreamChannel::OnAuthenticationDone, base::Unretained(this))); } -void PepperStreamChannel::OnSSLConnect(int result) { - DCHECK(CalledOnValidThread()); - if (result != net::OK) { - LOG(ERROR) << "Error during SSL connection: " << result; +void PepperStreamChannel::OnAuthenticationDone( + net::Error error, net::StreamSocket* socket) { + DCHECK(CalledOnValidThread()); + if (error != net::OK) { NotifyConnectFailed(); return; } - DCHECK(socket_->IsConnected()); - AuthenticateChannel(); -} - -void PepperStreamChannel::AuthenticateChannel() { - DCHECK(CalledOnValidThread()); - - authenticator_.reset( - new ClientChannelAuthenticator(session_->shared_secret())); - authenticator_->Authenticate(ssl_client_socket_, base::Bind( - &PepperStreamChannel::OnAuthenticationDone, base::Unretained(this))); -} - -void PepperStreamChannel::OnAuthenticationDone( - ChannelAuthenticator::Result result) { - DCHECK(CalledOnValidThread()); - - switch (result) { - case ChannelAuthenticator::SUCCESS: - NotifyConnected(socket_.release()); - break; - - case ChannelAuthenticator::FAILURE: - NotifyConnectFailed(); - break; - } + NotifyConnected(socket); } void PepperStreamChannel::NotifyConnected(net::StreamSocket* socket) { @@ -271,7 +195,7 @@ void PepperStreamChannel::NotifyConnected(net::StreamSocket* socket) { void PepperStreamChannel::NotifyConnectFailed() { channel_ = NULL; owned_channel_.reset(); - socket_.reset(); + authenticator_.reset(); NotifyConnected(NULL); } diff --git a/remoting/protocol/pepper_stream_channel.h b/remoting/protocol/pepper_stream_channel.h index dbe206e..5c17e4f 100644 --- a/remoting/protocol/pepper_stream_channel.h +++ b/remoting/protocol/pepper_stream_channel.h @@ -37,7 +37,7 @@ class PepperStreamChannel : public PepperChannel, // PepperChannel implementation. virtual void Connect(pp::Instance* pp_instance, const TransportConfig& transport_config, - const std::string& remote_cert) OVERRIDE; + ChannelAuthenticator* authenticator) OVERRIDE; virtual void AddRemoveCandidate(const cricket::Candidate& candidate) OVERRIDE; virtual const std::string& name() const OVERRIDE; virtual bool is_connected() const OVERRIDE; @@ -49,12 +49,7 @@ class PepperStreamChannel : public PepperChannel, private: void OnP2PConnect(int result); - - bool EstablishSSLConnection(); - void OnSSLConnect(int result); - - void AuthenticateChannel(); - void OnAuthenticationDone(ChannelAuthenticator::Result result); + void OnAuthenticationDone(net::Error error, net::StreamSocket* socket); void NotifyConnected(net::StreamSocket* socket); void NotifyConnectFailed(); @@ -62,28 +57,18 @@ class PepperStreamChannel : public PepperChannel, PepperSession* session_; std::string name_; Session::StreamChannelCallback callback_; - - std::string remote_cert_; + scoped_ptr<ChannelAuthenticator> authenticator_; // We own |channel_| until it is connected. After that - // SSLClientSocket owns it. + // |authenticator_| owns it. scoped_ptr<PepperTransportSocketAdapter> owned_channel_; PepperTransportSocketAdapter* channel_; // Indicates that we've finished connecting. bool connected_; - scoped_ptr<net::StreamSocket> socket_; - net::SSLClientSocket* ssl_client_socket_; - - // Used to verify the certificate received in SSLClientSocket. - scoped_ptr<net::CertVerifier> cert_verifier_; - - scoped_ptr<ChannelAuthenticator> authenticator_; - - // Callback called by the TCP and SSL layers. + // Callback called by the TCP layer. net::OldCompletionCallbackImpl<PepperStreamChannel> p2p_connect_callback_; - net::OldCompletionCallbackImpl<PepperStreamChannel> ssl_connect_callback_; DISALLOW_COPY_AND_ASSIGN(PepperStreamChannel); }; diff --git a/remoting/protocol/v1_client_channel_authenticator.cc b/remoting/protocol/v1_client_channel_authenticator.cc new file mode 100644 index 0000000..312403b --- /dev/null +++ b/remoting/protocol/v1_client_channel_authenticator.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2011 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 "remoting/protocol/v1_client_channel_authenticator.h" + +#include "net/base/cert_verifier.h" +#include "net/base/host_port_pair.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/ssl_config_service.h" +#include "net/socket/client_socket_factory.h" +#include "net/socket/ssl_client_socket.h" +#include "remoting/protocol/auth_util.h" + +namespace remoting { +namespace protocol { + +V1ClientChannelAuthenticator::V1ClientChannelAuthenticator( + const std::string& host_cert, + const std::string& shared_secret) + : host_cert_(host_cert), + shared_secret_(shared_secret), + socket_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(connect_callback_( + this, &V1ClientChannelAuthenticator::OnConnected)), + ALLOW_THIS_IN_INITIALIZER_LIST(auth_write_callback_( + this, &V1ClientChannelAuthenticator::OnAuthBytesWritten)) { +} + +V1ClientChannelAuthenticator::~V1ClientChannelAuthenticator() { +} + +void V1ClientChannelAuthenticator::SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) { + DCHECK(CalledOnValidThread()); + DCHECK(socket->IsConnected()); + + done_callback_ = done_callback; + + cert_verifier_.reset(new net::CertVerifier()); + + net::SSLConfig::CertAndStatus cert_and_status; + cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; + cert_and_status.der_cert = host_cert_; + + net::SSLConfig ssl_config; + // Certificate verification and revocation checking are not needed + // because we use self-signed certs. Disable it so that the SSL + // layer doesn't try to initialize OCSP (OCSP works only on the IO + // thread). + ssl_config.allowed_bad_certs.push_back(cert_and_status); + ssl_config.rev_checking_enabled = false; + + net::HostPortPair host_and_port(kSslFakeHostName, 0); + net::SSLClientSocketContext context; + context.cert_verifier = cert_verifier_.get(); + socket_.reset( + net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket( + socket, host_and_port, ssl_config, NULL, context)); + + int result = socket_->Connect(&connect_callback_); + if (result == net::ERR_IO_PENDING) + return; + OnConnected(result); +} + +void V1ClientChannelAuthenticator::OnConnected(int result) { + if (result != net::OK) { + LOG(ERROR) << "Failed to establish SSL connection"; + done_callback_.Run(static_cast<net::Error>(result), NULL); + } + + // Get keying material from SSL. + unsigned char key_material[kAuthDigestLength]; + int export_result = socket_->ExportKeyingMaterial( + kClientAuthSslExporterLabel, "", key_material, kAuthDigestLength); + if (export_result != net::OK) { + LOG(ERROR) << "Error fetching keying material: " << export_result; + done_callback_.Run(static_cast<net::Error>(export_result), NULL); + return; + } + + // Generate authentication digest to write to the socket. + std::string auth_bytes; + if (!GetAuthBytes(shared_secret_, + std::string(reinterpret_cast<char*>(key_material), + kAuthDigestLength), + &auth_bytes)) { + done_callback_.Run(net::ERR_FAILED, NULL); + return; + } + + // Allocate a buffer to write the digest. + auth_write_buf_ = new net::DrainableIOBuffer( + new net::StringIOBuffer(auth_bytes), auth_bytes.size()); + WriteAuthenticationBytes(); +} + +void V1ClientChannelAuthenticator::WriteAuthenticationBytes() { + while (true) { + int result = socket_->Write(auth_write_buf_, + auth_write_buf_->BytesRemaining(), + &auth_write_callback_); + if (result == net::ERR_IO_PENDING) + break; + if (!HandleAuthBytesWritten(result)) + break; + } +} + +void V1ClientChannelAuthenticator::OnAuthBytesWritten(int result) { + DCHECK(CalledOnValidThread()); + + if (HandleAuthBytesWritten(result)) + WriteAuthenticationBytes(); +} + +bool V1ClientChannelAuthenticator::HandleAuthBytesWritten(int result) { + if (result <= 0) { + LOG(ERROR) << "Error writing authentication: " << result; + done_callback_.Run(static_cast<net::Error>(result), NULL); + return false; + } + + auth_write_buf_->DidConsume(result); + if (auth_write_buf_->BytesRemaining() > 0) + return true; + + done_callback_.Run(net::OK, socket_.release()); + return false; +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/v1_client_channel_authenticator.h b/remoting/protocol/v1_client_channel_authenticator.h new file mode 100644 index 0000000..d8b2659 --- /dev/null +++ b/remoting/protocol/v1_client_channel_authenticator.h @@ -0,0 +1,63 @@ +// Copyright (c) 2011 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 REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_ +#define REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_ + +#include <string> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" +#include "remoting/protocol/channel_authenticator.h" + +namespace net { +class CertVerifier; +class DrainableIOBuffer; +class GrowableIOBuffer; +class SSLClientSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +class V1ClientChannelAuthenticator : public ChannelAuthenticator, + public base::NonThreadSafe { + public: + V1ClientChannelAuthenticator(const std::string& host_cert, + const std::string& shared_secret); + virtual ~V1ClientChannelAuthenticator(); + + // ChannelAuthenticator implementation. + virtual void SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE; + + private: + void OnConnected(int result); + void WriteAuthenticationBytes(); + void OnAuthBytesWritten(int result); + bool HandleAuthBytesWritten(int result); + + std::string host_cert_; + std::string shared_secret_; + scoped_ptr<net::SSLClientSocket> socket_; + DoneCallback done_callback_; + + scoped_ptr<net::CertVerifier> cert_verifier_; + scoped_refptr<net::DrainableIOBuffer> auth_write_buf_; + + net::OldCompletionCallbackImpl<V1ClientChannelAuthenticator> + connect_callback_; + net::OldCompletionCallbackImpl<V1ClientChannelAuthenticator> + auth_write_callback_; + + DISALLOW_COPY_AND_ASSIGN(V1ClientChannelAuthenticator); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_V1_CLIENT_CHANNEL_AUTHENTICATOR_H_ diff --git a/remoting/protocol/v1_host_channel_authenticator.cc b/remoting/protocol/v1_host_channel_authenticator.cc new file mode 100644 index 0000000..c124d47 --- /dev/null +++ b/remoting/protocol/v1_host_channel_authenticator.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2011 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 "remoting/protocol/v1_host_channel_authenticator.h" + +#include "crypto/rsa_private_key.h" +#include "crypto/secure_util.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/ssl_config_service.h" +#include "net/base/x509_certificate.h" +#include "net/socket/ssl_server_socket.h" +#include "remoting/protocol/auth_util.h" + +namespace remoting { +namespace protocol { + +V1HostChannelAuthenticator::V1HostChannelAuthenticator( + const std::string& local_cert, + crypto::RSAPrivateKey* local_private_key, + const std::string& shared_secret) + : local_cert_(local_cert), + local_private_key_(local_private_key), + shared_secret_(shared_secret), + socket_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(connect_callback_( + this, &V1HostChannelAuthenticator::OnConnected)), + ALLOW_THIS_IN_INITIALIZER_LIST(auth_read_callback_( + this, &V1HostChannelAuthenticator::OnAuthBytesRead)) { +} + +V1HostChannelAuthenticator::~V1HostChannelAuthenticator() { +} + +void V1HostChannelAuthenticator::SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) { + DCHECK(CalledOnValidThread()); + + scoped_ptr<net::StreamSocket> channel_socket(socket); + done_callback_ = done_callback; + + scoped_refptr<net::X509Certificate> cert = + net::X509Certificate::CreateFromBytes( + local_cert_.data(), local_cert_.length()); + if (!cert) { + LOG(ERROR) << "Failed to parse X509Certificate"; + done_callback.Run(net::ERR_FAILED, NULL); + return; + } + + net::SSLConfig ssl_config; + socket_.reset(net::CreateSSLServerSocket( + channel_socket.release(), cert, local_private_key_, ssl_config)); + + int result = socket_->Handshake(&connect_callback_); + if (result == net::ERR_IO_PENDING) { + return; + } + OnConnected(result); +} + +void V1HostChannelAuthenticator::OnConnected(int result) { + if (result != net::OK) { + LOG(ERROR) << "Failed to establish SSL connection"; + done_callback_.Run(static_cast<net::Error>(result), NULL); + } + + // Read an authentication digest. + auth_read_buf_ = new net::GrowableIOBuffer(); + auth_read_buf_->SetCapacity(kAuthDigestLength); + DoAuthRead(); +} + +void V1HostChannelAuthenticator::DoAuthRead(){ + while (true) { + int result = socket_->Read(auth_read_buf_, + auth_read_buf_->RemainingCapacity(), + &auth_read_callback_); + if (result == net::ERR_IO_PENDING) + break; + if (!HandleAuthBytesRead(result)) + break; + } +} + +void V1HostChannelAuthenticator::OnAuthBytesRead(int result) { + DCHECK(CalledOnValidThread()); + + if (HandleAuthBytesRead(result)) + DoAuthRead(); +} + +bool V1HostChannelAuthenticator::HandleAuthBytesRead(int read_result) { + if (read_result <= 0) { + done_callback_.Run(static_cast<net::Error>(read_result), NULL); + return false; + } + + auth_read_buf_->set_offset(auth_read_buf_->offset() + read_result); + if (auth_read_buf_->RemainingCapacity() > 0) + return true; + + if (!VerifyAuthBytes(std::string( + auth_read_buf_->StartOfBuffer(), + auth_read_buf_->StartOfBuffer() + kAuthDigestLength))) { + LOG(WARNING) << "Mismatched authentication"; + done_callback_.Run(net::ERR_FAILED, NULL); + return false; + } + + done_callback_.Run(net::OK, socket_.release()); + return false; +} + +bool V1HostChannelAuthenticator::VerifyAuthBytes( + const std::string& received_auth_bytes) { + DCHECK(received_auth_bytes.length() == kAuthDigestLength); + + unsigned char key_material[kAuthDigestLength]; + int export_result = socket_->ExportKeyingMaterial( + kClientAuthSslExporterLabel, "", key_material, kAuthDigestLength); + if (export_result != net::OK) { + LOG(ERROR) << "Error fetching keying material: " << export_result; + done_callback_.Run(static_cast<net::Error>(export_result), NULL); + return false; + } + + std::string auth_bytes; + if (!GetAuthBytes(shared_secret_, + std::string(key_material, key_material + kAuthDigestLength), + &auth_bytes)) { + done_callback_.Run(net::ERR_FAILED, NULL); + return false; + } + + return crypto::SecureMemEqual(received_auth_bytes.data(), + &(auth_bytes[0]), kAuthDigestLength); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/v1_host_channel_authenticator.h b/remoting/protocol/v1_host_channel_authenticator.h new file mode 100644 index 0000000..081eff7 --- /dev/null +++ b/remoting/protocol/v1_host_channel_authenticator.h @@ -0,0 +1,67 @@ +// Copyright (c) 2011 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 REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_ +#define REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_ + +#include "remoting/protocol/channel_authenticator.h" + +#include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" + +namespace crypto { +class RSAPrivateKey; +} // namespace crypto + +namespace net { +class GrowableIOBuffer; +class SSLServerSocket; +class SSLSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +class V1HostChannelAuthenticator : public ChannelAuthenticator, + public base::NonThreadSafe { + public: + // Caller retains ownership of |local_private_key|. It must exist + // while this object exists. + V1HostChannelAuthenticator(const std::string& local_cert, + crypto::RSAPrivateKey* local_private_key, + const std::string& shared_secret); + virtual ~V1HostChannelAuthenticator(); + + // ChannelAuthenticator interface. + virtual void SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE; + + private: + void OnConnected(int result); + void DoAuthRead(); + void OnAuthBytesRead(int result); + bool HandleAuthBytesRead(int result); + bool VerifyAuthBytes(const std::string& received_auth_bytes); + + std::string local_cert_; + crypto::RSAPrivateKey* local_private_key_; + std::string shared_secret_; + scoped_ptr<net::SSLServerSocket> socket_; + DoneCallback done_callback_; + + scoped_refptr<net::GrowableIOBuffer> auth_read_buf_; + + net::OldCompletionCallbackImpl<V1HostChannelAuthenticator> + connect_callback_; + net::OldCompletionCallbackImpl<V1HostChannelAuthenticator> + auth_read_callback_; + + DISALLOW_COPY_AND_ASSIGN(V1HostChannelAuthenticator); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_ |