diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 13:06:04 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 13:06:04 +0000 |
commit | 34deb103e128d7081e1a3459c0d24c1582290dac (patch) | |
tree | 20efa74c0213158808a815300557a6b71f26e909 /remoting | |
parent | d6be33ef12e0768fb8c4e7ad779854f98d913840 (diff) | |
download | chromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.zip chromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.tar.gz chromium_src-34deb103e128d7081e1a3459c0d24c1582290dac.tar.bz2 |
Replace V1*ChannelAuthenticator with SslHmacChannelAuthenticator.
The new class will be used for both V1 and V2 authentication
BUG=105214
Review URL: http://codereview.chromium.org/8963005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115519 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/protocol/auth_util.cc | 28 | ||||
-rw-r--r-- | remoting/protocol/auth_util.h | 16 | ||||
-rw-r--r-- | remoting/protocol/channel_authenticator.h | 2 | ||||
-rw-r--r-- | remoting/protocol/pepper_session.cc | 1 | ||||
-rw-r--r-- | remoting/protocol/ssl_hmac_channel_authenticator.cc | 272 | ||||
-rw-r--r-- | remoting/protocol/ssl_hmac_channel_authenticator.h | 110 | ||||
-rw-r--r-- | remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc | 151 | ||||
-rw-r--r-- | remoting/protocol/v1_authenticator.cc | 16 | ||||
-rw-r--r-- | remoting/protocol/v1_authenticator_unittest.cc | 18 | ||||
-rw-r--r-- | remoting/protocol/v1_client_channel_authenticator.cc | 134 | ||||
-rw-r--r-- | remoting/protocol/v1_client_channel_authenticator.h | 58 | ||||
-rw-r--r-- | remoting/protocol/v1_host_channel_authenticator.cc | 144 | ||||
-rw-r--r-- | remoting/protocol/v1_host_channel_authenticator.h | 61 | ||||
-rw-r--r-- | remoting/remoting.gyp | 7 |
14 files changed, 584 insertions, 434 deletions
diff --git a/remoting/protocol/auth_util.cc b/remoting/protocol/auth_util.cc index 51f7af5..281dabc 100644 --- a/remoting/protocol/auth_util.cc +++ b/remoting/protocol/auth_util.cc @@ -9,12 +9,16 @@ #include "base/string_util.h" #include "crypto/hmac.h" #include "crypto/sha2.h" +#include "net/base/net_errors.h" +#include "net/socket/ssl_socket.h" namespace remoting { namespace protocol { const char kClientAuthSslExporterLabel[] = "EXPORTER-remoting-channel-auth-client"; +const char kHostAuthSslExporterLabel[] = + "EXPORTER-remoting-channel-auth-host"; const char kSslFakeHostName[] = "chromoting"; @@ -37,23 +41,31 @@ bool VerifySupportAuthToken(const std::string& jid, } // static -bool GetAuthBytes(const std::string& shared_secret, - const std::string& key_material, - std::string* auth_bytes) { +std::string GetAuthBytes(net::SSLSocket* socket, + const base::StringPiece& label, + const base::StringPiece& shared_secret) { + // Get keying material from SSL. + unsigned char key_material[kAuthDigestLength]; + int export_result = socket->ExportKeyingMaterial( + label, "", key_material, kAuthDigestLength); + if (export_result != net::OK) { + LOG(ERROR) << "Error fetching keying material: " << export_result; + return std::string(); + } + // Generate auth digest based on the keying material and shared secret. crypto::HMAC response(crypto::HMAC::SHA256); - if (!response.Init(key_material)) { + if (!response.Init(key_material, kAuthDigestLength)) { NOTREACHED() << "HMAC::Init failed"; - return false; + return std::string(); } unsigned char out_bytes[kAuthDigestLength]; if (!response.Sign(shared_secret, out_bytes, kAuthDigestLength)) { NOTREACHED() << "HMAC::Sign failed"; - return false; + return std::string(); } - auth_bytes->assign(out_bytes, out_bytes + kAuthDigestLength); - return true; + return std::string(out_bytes, out_bytes + kAuthDigestLength); } } // namespace protocol diff --git a/remoting/protocol/auth_util.h b/remoting/protocol/auth_util.h index 7a81c12..e33a473 100644 --- a/remoting/protocol/auth_util.h +++ b/remoting/protocol/auth_util.h @@ -7,11 +7,18 @@ #include <string> +#include "base/string_piece.h" + +namespace net { +class SSLSocket; +} // namespace net + namespace remoting { namespace protocol { // Labels for use when exporting the SSL master keys. extern const char kClientAuthSslExporterLabel[]; +extern const char kHostAuthSslExporterLabel[]; // Fake hostname used for SSL connections. extern const char kSslFakeHostName[]; @@ -28,10 +35,11 @@ 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); +// Returns authentication bytes that must be used for the given +// |socket|. Empty string is returned in case of failure. +std::string GetAuthBytes(net::SSLSocket* socket, + const base::StringPiece& label, + const base::StringPiece& shared_secret); } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/channel_authenticator.h b/remoting/protocol/channel_authenticator.h index 535730a..2ef66de 100644 --- a/remoting/protocol/channel_authenticator.h +++ b/remoting/protocol/channel_authenticator.h @@ -7,7 +7,7 @@ #include <string> -#include "base/callback.h" +#include "base/callback_forward.h" #include "net/base/net_errors.h" namespace net { diff --git a/remoting/protocol/pepper_session.cc b/remoting/protocol/pepper_session.cc index 8eabb09..0cdfdd9 100644 --- a/remoting/protocol/pepper_session.cc +++ b/remoting/protocol/pepper_session.cc @@ -15,7 +15,6 @@ #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" diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.cc b/remoting/protocol/ssl_hmac_channel_authenticator.cc new file mode 100644 index 0000000..f38830a --- /dev/null +++ b/remoting/protocol/ssl_hmac_channel_authenticator.cc @@ -0,0 +1,272 @@ +// 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/ssl_hmac_channel_authenticator.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "crypto/secure_util.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/base/x509_certificate.h" +#include "net/socket/client_socket_factory.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_server_socket.h" +#include "remoting/protocol/auth_util.h" + +namespace remoting { +namespace protocol { + +// static +SslHmacChannelAuthenticator* SslHmacChannelAuthenticator::CreateForClient( + const std::string& remote_cert, + const std::string& auth_key) { + SslHmacChannelAuthenticator* result = + new SslHmacChannelAuthenticator(auth_key); + result->remote_cert_ = remote_cert; + return result; +} + +SslHmacChannelAuthenticator* SslHmacChannelAuthenticator::CreateForHost( + const std::string& local_cert, + crypto::RSAPrivateKey* local_private_key, + const std::string& auth_key) { + SslHmacChannelAuthenticator* result = + new SslHmacChannelAuthenticator(auth_key); + result->local_cert_ = local_cert; + result->local_private_key_ = local_private_key; + return result; +} + +SslHmacChannelAuthenticator::SslHmacChannelAuthenticator( + const std::string& auth_key) + : auth_key_(auth_key), + local_private_key_(NULL), + legacy_mode_(NONE) { +} + +SslHmacChannelAuthenticator::~SslHmacChannelAuthenticator() { +} + +void SslHmacChannelAuthenticator::SetLegacyOneWayMode(LegacyMode legacy_mode) { + // Must be called before SecureAndAuthenticate(). + DCHECK(done_callback_.is_null()); + legacy_mode_ = legacy_mode; +} + +void SslHmacChannelAuthenticator::SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) { + DCHECK(CalledOnValidThread()); + DCHECK(socket->IsConnected()); + + scoped_ptr<net::StreamSocket> channel_socket(socket); + done_callback_ = done_callback; + + int result; + if (is_ssl_server()) { + 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; + net::SSLServerSocket* server_socket = net::CreateSSLServerSocket( + channel_socket.release(), cert, local_private_key_, ssl_config); + socket_.reset(server_socket); + + result = server_socket->Handshake(base::Bind( + &SslHmacChannelAuthenticator::OnConnected, base::Unretained(this))); + } else { + 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 = remote_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( + channel_socket.release(), host_and_port, + ssl_config, NULL, context)); + + result = socket_->Connect( + base::Bind(&SslHmacChannelAuthenticator::OnConnected, + base::Unretained(this))); + } + + if (result == net::ERR_IO_PENDING) + return; + + OnConnected(result); +} + +bool SslHmacChannelAuthenticator::is_ssl_server() { + return local_private_key_ != NULL; +} + +void SslHmacChannelAuthenticator::OnConnected(int result) { + if (result != net::OK) { + LOG(WARNING) << "Failed to establish SSL connection"; + done_callback_.Run(static_cast<net::Error>(result), NULL); + return; + } + + if (legacy_mode_ != RECEIVE_ONLY) { + // Generate authentication digest to write to the socket. + std::string auth_bytes = GetAuthBytes( + socket_.get(), is_ssl_server() ? + kClientAuthSslExporterLabel : kHostAuthSslExporterLabel, auth_key_); + if (auth_bytes.empty()) { + 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()); + } + + if (legacy_mode_ != SEND_ONLY) { + // Read an incoming token. + auth_read_buf_ = new net::GrowableIOBuffer(); + auth_read_buf_->SetCapacity(kAuthDigestLength); + } + + // If WriteAuthenticationBytes() results in |done_callback_| being + // called then we must not do anything else because this object may + // be destroyed at that point. + bool callback_called = false; + if (legacy_mode_ != RECEIVE_ONLY) + WriteAuthenticationBytes(&callback_called); + if (!callback_called && legacy_mode_ != SEND_ONLY) + ReadAuthenticationBytes(); +} + +void SslHmacChannelAuthenticator::WriteAuthenticationBytes( + bool* callback_called) { + while (true) { + int result = socket_->Write( + auth_write_buf_, auth_write_buf_->BytesRemaining(), + base::Bind(&SslHmacChannelAuthenticator::OnAuthBytesWritten, + base::Unretained(this))); + if (result == net::ERR_IO_PENDING) + break; + if (!HandleAuthBytesWritten(result, callback_called)) + break; + } +} + +void SslHmacChannelAuthenticator::OnAuthBytesWritten(int result) { + DCHECK(CalledOnValidThread()); + + if (HandleAuthBytesWritten(result, NULL)) + WriteAuthenticationBytes(NULL); +} + +bool SslHmacChannelAuthenticator::HandleAuthBytesWritten( + int result, bool* callback_called) { + if (result <= 0) { + LOG(ERROR) << "Error writing authentication: " << result; + if (callback_called) + *callback_called = false; + done_callback_.Run(static_cast<net::Error>(result), NULL); + return false; + } + + auth_write_buf_->DidConsume(result); + if (auth_write_buf_->BytesRemaining() > 0) + return true; + + auth_write_buf_ = NULL; + CheckDone(callback_called); + return false; +} + +void SslHmacChannelAuthenticator::ReadAuthenticationBytes() { + while (true) { + int result = socket_->Read( + auth_read_buf_, + auth_read_buf_->RemainingCapacity(), + base::Bind(&SslHmacChannelAuthenticator::OnAuthBytesRead, + base::Unretained(this))); + if (result == net::ERR_IO_PENDING) + break; + if (!HandleAuthBytesRead(result)) + break; + } +} + +void SslHmacChannelAuthenticator::OnAuthBytesRead(int result) { + DCHECK(CalledOnValidThread()); + + if (HandleAuthBytesRead(result)) + ReadAuthenticationBytes(); +} + +bool SslHmacChannelAuthenticator::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; + } + + auth_read_buf_ = NULL; + CheckDone(NULL); + return false; +} + +bool SslHmacChannelAuthenticator::VerifyAuthBytes( + const std::string& received_auth_bytes) { + DCHECK(received_auth_bytes.length() == kAuthDigestLength); + + // Compute expected auth bytes. + std::string auth_bytes = GetAuthBytes( + socket_.get(), is_ssl_server() ? + kHostAuthSslExporterLabel : kClientAuthSslExporterLabel, auth_key_); + if (auth_bytes.empty()) + return false; + + return crypto::SecureMemEqual(received_auth_bytes.data(), + &(auth_bytes[0]), kAuthDigestLength); +} + +void SslHmacChannelAuthenticator::CheckDone(bool* callback_called) { + if (auth_write_buf_ == NULL && auth_read_buf_ == NULL) { + DCHECK(socket_.get() != NULL); + if (callback_called) + *callback_called = true; + done_callback_.Run(net::OK, socket_.release()); + } +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/ssl_hmac_channel_authenticator.h b/remoting/protocol/ssl_hmac_channel_authenticator.h new file mode 100644 index 0000000..e86e25e --- /dev/null +++ b/remoting/protocol/ssl_hmac_channel_authenticator.h @@ -0,0 +1,110 @@ +// 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_SSL_HMAC_CHANNEL_AUTHENTICATOR_H_ +#define REMOTING_PROTOCOL_SSL_HMAC_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 "remoting/protocol/channel_authenticator.h" + +namespace crypto { +class RSAPrivateKey; +} // namespace crypto + +namespace net { +class CertVerifier; +class DrainableIOBuffer; +class GrowableIOBuffer; +class SSLSocket; +} // namespace net + +namespace remoting { +namespace protocol { + +// SslHmacChannelAuthenticator implements ChannelAuthenticator that +// secures channels using SSL and authenticates them with a shared +// secret HMAC. +class SslHmacChannelAuthenticator : public ChannelAuthenticator, + public base::NonThreadSafe { + public: + enum LegacyMode { + NONE, + SEND_ONLY, + RECEIVE_ONLY, + }; + + // CreateForClient() and CreateForHost() create an authenticator + // instances for client and host. |auth_key| specifies shared key + // known by both host and client. In case of V1Authenticator the + // |auth_key| is set to access code. For EKE-based authentication + // |auth_key| is the key established using EKE over the signaling + // channel. + static SslHmacChannelAuthenticator* CreateForClient( + const std::string& remote_cert, + const std::string& auth_key); + + static SslHmacChannelAuthenticator* CreateForHost( + const std::string& local_cert, + crypto::RSAPrivateKey* local_private_key, + const std::string& auth_key); + + // TODO(sergeyu): This method is used only for the legacy + // V1Authenticator. Remove it when V1Authenticator is removed. + void SetLegacyOneWayMode(LegacyMode legacy_mode); + + virtual ~SslHmacChannelAuthenticator(); + + // ChannelAuthenticator interface. + virtual void SecureAndAuthenticate( + net::StreamSocket* socket, const DoneCallback& done_callback) OVERRIDE; + + private: + SslHmacChannelAuthenticator(const std::string& auth_key); + + bool is_ssl_server(); + + void OnConnected(int result); + + void WriteAuthenticationBytes(bool* callback_called); + void OnAuthBytesWritten(int result); + bool HandleAuthBytesWritten(int result, bool* callback_called); + + void ReadAuthenticationBytes(); + void OnAuthBytesRead(int result); + bool HandleAuthBytesRead(int result); + bool VerifyAuthBytes(const std::string& received_auth_bytes); + + void CheckDone(bool* callback_called); + + // The mutual secret used for authentication. + std::string auth_key_; + + // Used in the SERVER mode only. + std::string local_cert_; + crypto::RSAPrivateKey* local_private_key_; + + // Used in the CLIENT mode only. + std::string remote_cert_; + scoped_ptr<net::CertVerifier> cert_verifier_; + + LegacyMode legacy_mode_; + + scoped_ptr<net::SSLSocket> socket_; + DoneCallback done_callback_; + + scoped_refptr<net::DrainableIOBuffer> auth_write_buf_; + scoped_refptr<net::GrowableIOBuffer> auth_read_buf_; + + DISALLOW_COPY_AND_ASSIGN(SslHmacChannelAuthenticator); +}; + +} // namespace protocol +} // namespace remoting + +#endif // REMOTING_PROTOCOL_SSL_HMAC_CHANNEL_AUTHENTICATOR_H_ diff --git a/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc b/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc new file mode 100644 index 0000000..10fdcf7 --- /dev/null +++ b/remoting/protocol/ssl_hmac_channel_authenticator_unittest.cc @@ -0,0 +1,151 @@ +// 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/ssl_hmac_channel_authenticator.h" + +#include "base/bind.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "crypto/rsa_private_key.h" +#include "net/base/net_errors.h" +#include "remoting/protocol/connection_tester.h" +#include "remoting/protocol/fake_session.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" + +using testing::_; +using testing::SaveArg; + +namespace remoting { +namespace protocol { + +namespace { + +const char kTestSharedSecret[] = "1234-1234-5678"; +const char kTestSharedSecretBad[] = "0000-0000-0001"; + +class MockChannelDoneCallback { + public: + MOCK_METHOD2(OnDone, void(net::Error error, net::StreamSocket* socket)); +}; + +} // namespace + +class SslHmacChannelAuthenticatorTest : public testing::Test { + public: + SslHmacChannelAuthenticatorTest() { + } + virtual ~SslHmacChannelAuthenticatorTest() { + } + + protected: + virtual void SetUp() OVERRIDE { + FilePath certs_dir; + PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir); + certs_dir = certs_dir.AppendASCII("net"); + certs_dir = certs_dir.AppendASCII("data"); + certs_dir = certs_dir.AppendASCII("ssl"); + certs_dir = certs_dir.AppendASCII("certificates"); + + FilePath cert_path = certs_dir.AppendASCII("unittest.selfsigned.der"); + ASSERT_TRUE(file_util::ReadFileToString(cert_path, &host_cert_)); + + FilePath key_path = certs_dir.AppendASCII("unittest.key.bin"); + std::string key_string; + ASSERT_TRUE(file_util::ReadFileToString(key_path, &key_string)); + std::vector<uint8> key_vector( + reinterpret_cast<const uint8*>(key_string.data()), + reinterpret_cast<const uint8*>(key_string.data() + + key_string.length())); + private_key_.reset( + crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector)); + } + + void RunChannelAuth(bool expected_fail) { + client_fake_socket_.reset(new FakeSocket()); + host_fake_socket_.reset(new FakeSocket()); + client_fake_socket_->PairWith(host_fake_socket_.get()); + + client_auth_->SecureAndAuthenticate( + client_fake_socket_.release(), + base::Bind(&MockChannelDoneCallback::OnDone, + base::Unretained(&client_callback_))); + + host_auth_->SecureAndAuthenticate( + host_fake_socket_.release(), + base::Bind(&MockChannelDoneCallback::OnDone, + base::Unretained(&host_callback_))); + + net::StreamSocket* client_socket = NULL; + net::StreamSocket* host_socket = NULL; + + if (expected_fail) { + EXPECT_CALL(client_callback_, OnDone(net::ERR_FAILED, NULL)); + EXPECT_CALL(host_callback_, OnDone(net::ERR_FAILED, NULL)); + } else { + EXPECT_CALL(client_callback_, OnDone(net::OK, _)) + .WillOnce(SaveArg<1>(&client_socket)); + EXPECT_CALL(host_callback_, OnDone(net::OK, _)) + .WillOnce(SaveArg<1>(&host_socket)); + } + + message_loop_.RunAllPending(); + + client_socket_.reset(client_socket); + host_socket_.reset(host_socket); + } + + MessageLoop message_loop_; + + scoped_ptr<crypto::RSAPrivateKey> private_key_; + std::string host_cert_; + scoped_ptr<FakeSocket> client_fake_socket_; + scoped_ptr<FakeSocket> host_fake_socket_; + scoped_ptr<ChannelAuthenticator> client_auth_; + scoped_ptr<ChannelAuthenticator> host_auth_; + MockChannelDoneCallback client_callback_; + MockChannelDoneCallback host_callback_; + scoped_ptr<net::StreamSocket> client_socket_; + scoped_ptr<net::StreamSocket> host_socket_; + + DISALLOW_COPY_AND_ASSIGN(SslHmacChannelAuthenticatorTest); +}; + +// Verify that a channel can be connected using a valid shared secret. +TEST_F(SslHmacChannelAuthenticatorTest, SuccessfulAuth) { + client_auth_.reset(SslHmacChannelAuthenticator::CreateForClient( + host_cert_, kTestSharedSecret)); + host_auth_.reset(SslHmacChannelAuthenticator::CreateForHost( + host_cert_, private_key_.get(), kTestSharedSecret)); + + RunChannelAuth(false); + + EXPECT_TRUE(client_socket_.get() != NULL); + EXPECT_TRUE(host_socket_.get() != NULL); + + StreamConnectionTester tester(host_socket_.get(), client_socket_.get(), + 100, 2); + + tester.Start(); + message_loop_.Run(); + tester.CheckResults(); +} + +// Verify that channels cannot be using invalid shared secret. +TEST_F(SslHmacChannelAuthenticatorTest, InvalidChannelSecret) { + client_auth_.reset(SslHmacChannelAuthenticator::CreateForClient( + host_cert_, kTestSharedSecretBad)); + host_auth_.reset(SslHmacChannelAuthenticator::CreateForHost( + host_cert_, private_key_.get(), kTestSharedSecret)); + + RunChannelAuth(true); + + EXPECT_TRUE(host_socket_.get() == NULL); +} + +} // namespace protocol +} // namespace remoting diff --git a/remoting/protocol/v1_authenticator.cc b/remoting/protocol/v1_authenticator.cc index afeb2d6..c39fd278 100644 --- a/remoting/protocol/v1_authenticator.cc +++ b/remoting/protocol/v1_authenticator.cc @@ -9,8 +9,7 @@ #include "crypto/rsa_private_key.h" #include "remoting/base/constants.h" #include "remoting/protocol/auth_util.h" -#include "remoting/protocol/v1_client_channel_authenticator.h" -#include "remoting/protocol/v1_host_channel_authenticator.h" +#include "remoting/protocol/ssl_hmac_channel_authenticator.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" using buzz::QName; @@ -78,7 +77,11 @@ XmlElement* V1ClientAuthenticator::GetNextMessage() { ChannelAuthenticator* V1ClientAuthenticator::CreateChannelAuthenticator() const { DCHECK_EQ(state_, ACCEPTED); - return new V1ClientChannelAuthenticator(remote_cert_, shared_secret_); + SslHmacChannelAuthenticator* result = + SslHmacChannelAuthenticator::CreateForClient( + remote_cert_, shared_secret_); + result->SetLegacyOneWayMode(SslHmacChannelAuthenticator::SEND_ONLY); + return result; }; V1HostAuthenticator::V1HostAuthenticator( @@ -134,8 +137,11 @@ XmlElement* V1HostAuthenticator::GetNextMessage() { ChannelAuthenticator* V1HostAuthenticator::CreateChannelAuthenticator() const { DCHECK_EQ(state_, ACCEPTED); - return new V1HostChannelAuthenticator( - local_cert_, local_private_key_.get(), shared_secret_); + SslHmacChannelAuthenticator* result = + SslHmacChannelAuthenticator::CreateForHost( + local_cert_, local_private_key_.get(), shared_secret_); + result->SetLegacyOneWayMode(SslHmacChannelAuthenticator::RECEIVE_ONLY); + return result; }; V1HostAuthenticatorFactory::V1HostAuthenticatorFactory( diff --git a/remoting/protocol/v1_authenticator_unittest.cc b/remoting/protocol/v1_authenticator_unittest.cc index b5295fd..5ffa317 100644 --- a/remoting/protocol/v1_authenticator_unittest.cc +++ b/remoting/protocol/v1_authenticator_unittest.cc @@ -15,14 +15,13 @@ #include "remoting/protocol/channel_authenticator.h" #include "remoting/protocol/connection_tester.h" #include "remoting/protocol/fake_session.h" -#include "remoting/protocol/v1_client_channel_authenticator.h" -#include "remoting/protocol/v1_host_channel_authenticator.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" using testing::_; using testing::DeleteArg; +using testing::Mock; using testing::SaveArg; namespace remoting { @@ -144,6 +143,9 @@ class V1AuthenticatorTest : public testing::Test { message_loop_.RunAllPending(); + Mock::VerifyAndClearExpectations(&client_callback_); + Mock::VerifyAndClearExpectations(&host_callback_); + client_socket_.reset(client_socket); host_socket_.reset(host_socket); } @@ -200,17 +202,5 @@ TEST_F(V1AuthenticatorTest, InvalidSecret) { ASSERT_EQ(Authenticator::REJECTED, host_->state()); } -// Verify that channels cannot be using invalid shared secret. -TEST_F(V1AuthenticatorTest, InvalidChannelSecret) { - client_auth_.reset(new V1ClientChannelAuthenticator( - host_cert_, kTestSharedSecretBad)); - host_auth_.reset(new V1HostChannelAuthenticator( - host_cert_, private_key_.get(),kTestSharedSecret)); - - RunChannelAuth(true); - - EXPECT_TRUE(host_socket_.get() == NULL); -} - } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/v1_client_channel_authenticator.cc b/remoting/protocol/v1_client_channel_authenticator.cc deleted file mode 100644 index 1f995fc..0000000 --- a/remoting/protocol/v1_client_channel_authenticator.cc +++ /dev/null @@ -1,134 +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/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) { -} - -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( - base::Bind(&V1ClientChannelAuthenticator::OnConnected, - base::Unretained(this))); - 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(), - base::Bind(&V1ClientChannelAuthenticator::OnAuthBytesWritten, - base::Unretained(this))); - 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 deleted file mode 100644 index c4b9494..0000000 --- a/remoting/protocol/v1_client_channel_authenticator.h +++ /dev/null @@ -1,58 +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. - -#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_; - - 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 deleted file mode 100644 index 3e30ee1..0000000 --- a/remoting/protocol/v1_host_channel_authenticator.cc +++ /dev/null @@ -1,144 +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/v1_host_channel_authenticator.h" - -#include "base/bind.h" -#include "base/bind_helpers.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) { -} - -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( - base::Bind(&V1HostChannelAuthenticator::OnConnected, - base::Unretained(this))); - 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(), - base::Bind(&V1HostChannelAuthenticator::OnAuthBytesRead, - base::Unretained(this))); - 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 deleted file mode 100644 index 56183ab..0000000 --- a/remoting/protocol/v1_host_channel_authenticator.h +++ /dev/null @@ -1,61 +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. - -#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" - -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_; - - DISALLOW_COPY_AND_ASSIGN(V1HostChannelAuthenticator); -}; - -} // namespace protocol -} // namespace remoting - -#endif // REMOTING_PROTOCOL_V1_HOST_CHANNEL_AUTHENTICATOR_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 5b2bc26..19e995e 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -814,16 +814,14 @@ 'protocol/session_manager.h', 'protocol/socket_reader_base.cc', 'protocol/socket_reader_base.h', + 'protocol/ssl_hmac_channel_authenticator.cc', + 'protocol/ssl_hmac_channel_authenticator.h', 'protocol/transport_config.cc', 'protocol/transport_config.h', 'protocol/util.cc', 'protocol/util.h', 'protocol/v1_authenticator.cc', 'protocol/v1_authenticator.h', - 'protocol/v1_client_channel_authenticator.cc', - 'protocol/v1_client_channel_authenticator.h', - 'protocol/v1_host_channel_authenticator.cc', - 'protocol/v1_host_channel_authenticator.h', 'protocol/video_reader.cc', 'protocol/video_reader.h', 'protocol/video_stub.h', @@ -946,6 +944,7 @@ 'protocol/ppapi_module_stub.cc', 'protocol/rtp_video_reader_unittest.cc', 'protocol/rtp_video_writer_unittest.cc', + 'protocol/ssl_hmac_channel_authenticator_unittest.cc', 'protocol/v1_authenticator_unittest.cc', 'run_all_unittests.cc', ], |