diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-02 01:54:08 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-02 01:54:08 +0000 |
commit | dd9ce7c7f312359bdc91ca6fcc158493bb478876 (patch) | |
tree | 3f81f316204c73f2cbaa84a27a0a8d7efeedd916 /remoting | |
parent | a0bd0e4d410000c1a4a888cd3af732d93e5fd9b0 (diff) | |
download | chromium_src-dd9ce7c7f312359bdc91ca6fcc158493bb478876.zip chromium_src-dd9ce7c7f312359bdc91ca6fcc158493bb478876.tar.gz chromium_src-dd9ce7c7f312359bdc91ca6fcc158493bb478876.tar.bz2 |
Separate channel authentication code from JingleStreamConnecter.
The new ChannelAuthenticator will also be used for pepper-based protocol
implementation.
BUG=51198
TEST=Unittests
Review URL: http://codereview.chromium.org/7537036
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95030 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/protocol/channel_authenticator.cc | 216 | ||||
-rw-r--r-- | remoting/protocol/channel_authenticator.h | 101 | ||||
-rw-r--r-- | remoting/protocol/jingle_stream_connector.cc | 169 | ||||
-rw-r--r-- | remoting/protocol/jingle_stream_connector.h | 18 | ||||
-rw-r--r-- | remoting/remoting.gyp | 2 |
5 files changed, 339 insertions, 167 deletions
diff --git a/remoting/protocol/channel_authenticator.cc b/remoting/protocol/channel_authenticator.cc new file mode 100644 index 0000000..fefab18 --- /dev/null +++ b/remoting/protocol/channel_authenticator.cc @@ -0,0 +1,216 @@ +// 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_client_socket.h" +#include "net/socket/ssl_server_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-1 authentication digest. +const size_t kAuthDigestLength = 20; + +// 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::SHA1); + if (!response.Init(shared_secret)) { + NOTREACHED() << "HMAC::Init failed"; + return false; + } + unsigned char out_bytes[kAuthDigestLength]; + if (!response.Sign(key_material, out_bytes, kAuthDigestLength)) { + NOTREACHED() << "HMAC::Sign failed"; + return false; + } + + auth_bytes->assign(out_bytes, out_bytes + kAuthDigestLength); + return true; +} + +} // namespace + +HostChannelAuthenticator::HostChannelAuthenticator(net::SSLServerSocket* socket) + : socket_(socket), + ALLOW_THIS_IN_INITIALIZER_LIST(auth_read_callback_( + this, &HostChannelAuthenticator::OnAuthBytesRead)) { +} + +HostChannelAuthenticator::~HostChannelAuthenticator() { +} + +void HostChannelAuthenticator::Authenticate(const std::string& shared_secret, + const DoneCallback& done_callback) { + DCHECK(CalledOnValidThread()); + + 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( + net::SSLClientSocket* socket) + : socket_(socket), + ALLOW_THIS_IN_INITIALIZER_LIST(auth_write_callback_( + this, &ClientChannelAuthenticator::OnAuthBytesWritten)) { +} + +ClientChannelAuthenticator::~ClientChannelAuthenticator() { +} + +void ClientChannelAuthenticator::Authenticate( + const std::string& shared_secret, + const DoneCallback& done_callback) { + DCHECK(CalledOnValidThread()); + + 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 new file mode 100644 index 0000000..af646bf --- /dev/null +++ b/remoting/protocol/channel_authenticator.h @@ -0,0 +1,101 @@ +// 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_CHANNEL_AUTHENTICATOR_H_ +#define REMOTING_PROTOCOL_CHANNEL_AUTHENTICATOR_H_ + +#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" + +namespace net { +class DrainableIOBuffer; +class GrowableIOBuffer; +class SSLClientSocket; +class SSLServerSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +class ChannelAuthenticator : public base::NonThreadSafe { + 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(const std::string& shared_secret, + const DoneCallback& done_callback) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(ChannelAuthenticator); +}; + +class HostChannelAuthenticator : public ChannelAuthenticator { + public: + HostChannelAuthenticator(net::SSLServerSocket* socket); + ~HostChannelAuthenticator(); + + // ChannelAuthenticator overrides. + virtual void Authenticate(const std::string& shared_secret, + 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 auth_bytes_; + net::SSLServerSocket* socket_; + DoneCallback done_callback_; + + scoped_refptr<net::GrowableIOBuffer> auth_read_buf_; + + net::CompletionCallbackImpl<HostChannelAuthenticator> auth_read_callback_; + + DISALLOW_COPY_AND_ASSIGN(HostChannelAuthenticator); +}; + +class ClientChannelAuthenticator : public ChannelAuthenticator { + public: + ClientChannelAuthenticator(net::SSLClientSocket* socket); + ~ClientChannelAuthenticator(); + + // ChannelAuthenticator overrides. + void Authenticate(const std::string& shared_secret, + const DoneCallback& done_callback); + + private: + void DoAuthWrite(); + void OnAuthBytesWritten(int result); + bool HandleAuthBytesWritten(int result); + + net::SSLClientSocket* socket_; + DoneCallback done_callback_; + + scoped_refptr<net::DrainableIOBuffer> auth_write_buf_; + + net::CompletionCallbackImpl<ClientChannelAuthenticator> auth_write_callback_; + + DISALLOW_COPY_AND_ASSIGN(ClientChannelAuthenticator); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_CHANNEL_AUTHENTICATOR_H_ diff --git a/remoting/protocol/jingle_stream_connector.cc b/remoting/protocol/jingle_stream_connector.cc index 849f432..01ba8f8 100644 --- a/remoting/protocol/jingle_stream_connector.cc +++ b/remoting/protocol/jingle_stream_connector.cc @@ -4,7 +4,7 @@ #include "remoting/protocol/jingle_stream_connector.h" -#include "crypto/hmac.h" +#include "base/bind.h" #include "jingle/glue/channel_socket_adapter.h" #include "jingle/glue/pseudotcp_adapter.h" #include "net/base/cert_status_flags.h" @@ -22,12 +22,6 @@ namespace protocol { namespace { -// Size of the HMAC-SHA-1 authentication digest. -const int kAuthDigestLength = 20; - -// Labels for use when exporting the SSL master keys. -const char kClientSslExporterLabel[] = "EXPORTER-remoting-channel-auth-client"; - // Value is choosen to balance the extra latency against the reduced // load due to ACK traffic. const int kTcpAckDelayMilliseconds = 10; @@ -76,11 +70,7 @@ JingleStreamConnector::JingleStreamConnector( ALLOW_THIS_IN_INITIALIZER_LIST(tcp_connect_callback_( this, &JingleStreamConnector::OnTCPConnect)), ALLOW_THIS_IN_INITIALIZER_LIST(ssl_connect_callback_( - this, &JingleStreamConnector::OnSSLConnect)), - ALLOW_THIS_IN_INITIALIZER_LIST(auth_write_callback_( - this, &JingleStreamConnector::OnAuthBytesWritten)), - ALLOW_THIS_IN_INITIALIZER_LIST(auth_read_callback_( - this, &JingleStreamConnector::OnAuthBytesRead)) { + this, &JingleStreamConnector::OnSSLConnect)) { } JingleStreamConnector::~JingleStreamConnector() { @@ -197,156 +187,29 @@ void JingleStreamConnector::OnSSLConnect(int result) { } void JingleStreamConnector::AuthenticateChannel() { - DCHECK(CalledOnValidThread()); if (initiator_) { - // Allocate a buffer to write the authentication digest. - scoped_refptr<net::IOBuffer> write_buf = - new net::IOBuffer(kAuthDigestLength); - auth_write_buf_ = new net::DrainableIOBuffer(write_buf, - kAuthDigestLength); - - // Generate the auth digest to send. - if (!GetAuthBytes(kClientSslExporterLabel, auth_write_buf_->data())) { - NotifyError(); - return; - } - - DoAuthWrite(); + authenticator_.reset(new ClientChannelAuthenticator(ssl_client_socket_)); } else { - // Read an authentication digest. - auth_read_buf_ = new net::GrowableIOBuffer(); - auth_read_buf_->SetCapacity(kAuthDigestLength); - DoAuthRead(); + authenticator_.reset(new HostChannelAuthenticator(ssl_server_socket_)); } -} -void JingleStreamConnector::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; - } + authenticator_->Authenticate( + session_->shared_secret(), + base::Bind(&JingleStreamConnector::OnAuthenticationDone, + base::Unretained(this))); } -void JingleStreamConnector::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)) +void JingleStreamConnector::OnAuthenticationDone( + ChannelAuthenticator::Result result) { + switch (result) { + case ChannelAuthenticator::SUCCESS: + NotifyDone(socket_.release()); break; - } -} - -void JingleStreamConnector::OnAuthBytesWritten(int result) { - if (HandleAuthBytesWritten(result)) - DoAuthWrite(); -} - -void JingleStreamConnector::OnAuthBytesRead(int result) { - if (HandleAuthBytesRead(result)) - DoAuthRead(); -} -bool JingleStreamConnector::HandleAuthBytesWritten(int result) { - DCHECK(CalledOnValidThread()); - - if (result <= 0) { - LOG(ERROR) << "Error writing authentication: " << result; - NotifyError(); - return false; - } - - auth_write_buf_->DidConsume(result); - if (auth_write_buf_->BytesRemaining() > 0) - return true; - - NotifyDone(socket_.release()); - return false; -} - -bool JingleStreamConnector::HandleAuthBytesRead(int read_result) { - DCHECK(CalledOnValidThread()); - - if (read_result <= 0) { - LOG(ERROR) << "Error reading authentication: " << read_result; - NotifyError(); - return false; - } - - auth_read_buf_->set_offset(auth_read_buf_->offset() + read_result); - if (auth_read_buf_->RemainingCapacity() > 0) - return true; - - if (!VerifyAuthBytes( - kClientSslExporterLabel, - auth_read_buf_->StartOfBuffer())) { - NotifyError(); - return false; - } - - NotifyDone(socket_.release()); - return false; -} - -bool JingleStreamConnector::VerifyAuthBytes(const char* label, - const char* auth_bytes) { - char expected[kAuthDigestLength]; - if (!GetAuthBytes(label, expected)) - return false; - // Compare the received and expected digests in fixed time, to limit the - // scope for timing attacks. - uint8 result = 0; - for (unsigned i = 0; i < sizeof(expected); i++) { - result |= auth_bytes[i] ^ expected[i]; - } - if (result != 0) { - LOG(ERROR) << "Mismatched authentication"; - return false; - } - return true; -} - -bool JingleStreamConnector::GetAuthBytes(const char* label, - char* out_bytes) { - // Fetch keying material from the socket. - unsigned char key_material[kAuthDigestLength]; - int result; - if (initiator_) { - result = ssl_client_socket_->ExportKeyingMaterial( - kClientSslExporterLabel, "", key_material, sizeof(key_material)); - } else { - result = ssl_server_socket_->ExportKeyingMaterial( - kClientSslExporterLabel, "", key_material, sizeof(key_material)); - } - if (result != net::OK) { - LOG(ERROR) << "Error fetching keying material: " << result; - return false; - } - - // Generate auth digest based on the keying material and shared secret. - crypto::HMAC response(crypto::HMAC::SHA1); - if (!response.Init(session_->shared_secret())) { - NOTREACHED() << "HMAC::Init failed"; - return false; - } - base::StringPiece message(reinterpret_cast<const char*>(key_material), - sizeof(key_material)); - if (!response.Sign( - message, - reinterpret_cast<unsigned char*>(out_bytes), - kAuthDigestLength)) { - NOTREACHED() << "HMAC::Sign failed"; - return false; + case ChannelAuthenticator::FAILURE: + NotifyError(); + break; } - - return true; } void JingleStreamConnector::NotifyDone(net::StreamSocket* socket) { diff --git a/remoting/protocol/jingle_stream_connector.h b/remoting/protocol/jingle_stream_connector.h index df003e3..6f7ce3b 100644 --- a/remoting/protocol/jingle_stream_connector.h +++ b/remoting/protocol/jingle_stream_connector.h @@ -5,9 +5,9 @@ #ifndef REMOTING_PROTOCOL_JINGLE_STREAM_CONNECTOR_H_ #define REMOTING_PROTOCOL_JINGLE_STREAM_CONNECTOR_H_ -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "net/base/completion_callback.h" +#include "remoting/protocol/channel_authenticator.h" #include "remoting/protocol/jingle_channel_connector.h" #include "remoting/protocol/session.h" @@ -61,14 +61,7 @@ class JingleStreamConnector : public JingleChannelConnector { void OnSSLConnect(int result); void AuthenticateChannel(); - void DoAuthWrite(); - void DoAuthRead(); - void OnAuthBytesWritten(int result); - void OnAuthBytesRead(int result); - bool HandleAuthBytesWritten(int result); - bool HandleAuthBytesRead(int result); - bool VerifyAuthBytes(const char* label, const char* auth_bytes); - bool GetAuthBytes(const char* label, char* out_bytes); + void OnAuthenticationDone(ChannelAuthenticator::Result result); void NotifyDone(net::StreamSocket* socket); void NotifyError(); @@ -82,9 +75,6 @@ class JingleStreamConnector : public JingleChannelConnector { std::string remote_cert_; crypto::RSAPrivateKey* local_private_key_; - scoped_refptr<net::DrainableIOBuffer> auth_write_buf_; - scoped_refptr<net::GrowableIOBuffer> auth_read_buf_; - cricket::TransportChannel* raw_channel_; scoped_ptr<net::StreamSocket> socket_; @@ -95,11 +85,11 @@ class JingleStreamConnector : public JingleChannelConnector { // 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::CompletionCallbackImpl<JingleStreamConnector> tcp_connect_callback_; net::CompletionCallbackImpl<JingleStreamConnector> ssl_connect_callback_; - net::CompletionCallbackImpl<JingleStreamConnector> auth_write_callback_; - net::CompletionCallbackImpl<JingleStreamConnector> auth_read_callback_; DISALLOW_COPY_AND_ASSIGN(JingleStreamConnector); }; diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 0bf9948..80799a8 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -580,6 +580,8 @@ 'protocol/auth_token_utils.h', 'protocol/buffered_socket_writer.cc', 'protocol/buffered_socket_writer.h', + 'protocol/channel_authenticator.cc', + 'protocol/channel_authenticator.h', 'protocol/client_control_sender.cc', 'protocol/client_control_sender.h', 'protocol/client_message_dispatcher.cc', |