summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/protocol/authenticator.h17
-rw-r--r--remoting/protocol/v1_authenticator.cc174
-rw-r--r--remoting/protocol/v1_authenticator.h89
-rw-r--r--remoting/protocol/v1_authenticator_unittest.cc121
-rw-r--r--remoting/remoting.gyp3
5 files changed, 399 insertions, 5 deletions
diff --git a/remoting/protocol/authenticator.h b/remoting/protocol/authenticator.h
index 4140e5a..0f382ed 100644
--- a/remoting/protocol/authenticator.h
+++ b/remoting/protocol/authenticator.h
@@ -14,6 +14,8 @@ class XmlElement;
namespace remoting {
namespace protocol {
+class ChannelAuthenticator;
+
// Authenticator is an abstract interface for authentication protocol
// implementations. Different implementations of this interface may be
// used on each side of the connection depending of type of the auth
@@ -37,7 +39,6 @@ class Authenticator {
// When GetNextMessage() is called:
// MESSAGE_READY -> WAITING_MESSAGE
// MESSAGE_READY -> ACCEPTED
- // MESSAGE_READY -> REJECTED
enum State {
// Waiting for the next message from the peer.
WAITING_MESSAGE,
@@ -59,12 +60,13 @@ class Authenticator {
virtual State state() const = 0;
// Called in response to incoming message received from the peer.
- // Should only be called when in WAITING_MESSAGE state.
- virtual void ProcessMessage(talk_base::XmlElement* message) = 0;
+ // Should only be called when in WAITING_MESSAGE state. Caller
+ // retains ownership of |message|.
+ virtual void ProcessMessage(const buzz::XmlElement* message) = 0;
// Must be called when in MESSAGE_READY state. Returns next
// authentication message that needs to be sent to the peer.
- virtual talk_base::XmlElement* GetNextMessage() = 0;
+ virtual buzz::XmlElement* GetNextMessage() = 0;
// Creates new authenticator for a channel. Caller must take
// ownership of the result. Can be called only in the ACCEPTED
@@ -74,6 +76,10 @@ class Authenticator {
// Factory for Authenticator instances.
class AuthenticatorFactory {
+ public:
+ AuthenticatorFactory() {}
+ virtual ~AuthenticatorFactory() {}
+
// Called when session-initiate stanza is received to create
// authenticator for the new session. |first_message| specifies
// authentication part of the session-initiate stanza so that
@@ -83,7 +89,8 @@ class AuthenticatorFactory {
// rejected. ProcessMessage() should be called with |first_message|
// for the result of this method.
virtual Authenticator* CreateAuthenticator(
- const talk_base::XmlElement* first_message) = 0;
+ const std::string& remote_jid,
+ const buzz::XmlElement* first_message) = 0;
};
} // namespace protocol
diff --git a/remoting/protocol/v1_authenticator.cc b/remoting/protocol/v1_authenticator.cc
new file mode 100644
index 0000000..2daf246
--- /dev/null
+++ b/remoting/protocol/v1_authenticator.cc
@@ -0,0 +1,174 @@
+// 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_authenticator.h"
+
+#include "base/base64.h"
+#include "base/logging.h"
+#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 "third_party/libjingle/source/talk/xmllite/xmlelement.h"
+
+using buzz::QName;
+using buzz::XmlElement;
+
+namespace remoting {
+namespace protocol {
+
+namespace {
+const char kAuthenticationTag[] = "authentication";
+const char kAuthTokenTag[] = "auth-token";
+const char kCertificateTag[] = "certificate";
+} // namespace
+
+V1ClientAuthenticator::V1ClientAuthenticator(
+ const std::string& local_jid,
+ const std::string& shared_secret)
+ : local_jid_(local_jid),
+ shared_secret_(shared_secret),
+ state_(MESSAGE_READY) {
+}
+
+V1ClientAuthenticator::~V1ClientAuthenticator() {
+}
+
+Authenticator::State V1ClientAuthenticator::state() const {
+ return state_;
+}
+
+void V1ClientAuthenticator::ProcessMessage(const XmlElement* message) {
+ DCHECK_EQ(state_, WAITING_MESSAGE);
+
+ // Parse the certificate.
+ const XmlElement* cert_tag =
+ message->FirstNamed(QName(kChromotingXmlNamespace, kCertificateTag));
+ if (cert_tag) {
+ std::string base64_cert = cert_tag->BodyText();
+ if (!base::Base64Decode(base64_cert, &remote_cert_)) {
+ LOG(ERROR) << "Failed to decode certificate received from the peer.";
+ remote_cert_.clear();
+ }
+ }
+
+ if (remote_cert_.empty()) {
+ state_ = REJECTED;
+ } else {
+ state_ = ACCEPTED;
+ }
+}
+
+XmlElement* V1ClientAuthenticator::GetNextMessage() {
+ DCHECK_EQ(state_, MESSAGE_READY);
+
+ XmlElement* authentication_tag = new XmlElement(
+ QName(kChromotingXmlNamespace, kAuthenticationTag));
+
+ std::string token =
+ protocol::GenerateSupportAuthToken(local_jid_, shared_secret_);
+
+ XmlElement* auth_token_tag = new XmlElement(
+ QName(kChromotingXmlNamespace, kAuthTokenTag));
+ auth_token_tag->SetBodyText(token);
+ authentication_tag->AddElement(auth_token_tag);
+
+ state_ = WAITING_MESSAGE;
+ return authentication_tag;
+}
+
+ChannelAuthenticator*
+V1ClientAuthenticator::CreateChannelAuthenticator() const {
+ DCHECK_EQ(state_, ACCEPTED);
+ return new V1ClientChannelAuthenticator(remote_cert_, shared_secret_);
+};
+
+V1HostAuthenticator::V1HostAuthenticator(
+ const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& shared_secret,
+ const std::string& remote_jid)
+ : local_cert_(local_cert),
+ local_private_key_(local_private_key),
+ shared_secret_(shared_secret),
+ remote_jid_(remote_jid),
+ state_(WAITING_MESSAGE) {
+}
+
+V1HostAuthenticator::~V1HostAuthenticator() {
+}
+
+Authenticator::State V1HostAuthenticator::state() const {
+ return state_;
+}
+
+void V1HostAuthenticator::ProcessMessage(const XmlElement* message) {
+ DCHECK_EQ(state_, WAITING_MESSAGE);
+
+ std::string auth_token =
+ message->TextNamed(buzz::QName(kChromotingXmlNamespace, kAuthTokenTag));
+
+ if (!protocol::VerifySupportAuthToken(
+ remote_jid_, shared_secret_, auth_token)) {
+ state_ = REJECTED;
+ } else {
+ state_ = MESSAGE_READY;
+ }
+}
+
+XmlElement* V1HostAuthenticator::GetNextMessage() {
+ DCHECK_EQ(state_, MESSAGE_READY);
+
+ XmlElement* message = new XmlElement(
+ QName(kChromotingXmlNamespace, kAuthenticationTag));
+
+ buzz::XmlElement* certificate_tag = new XmlElement(
+ buzz::QName(kChromotingXmlNamespace, kCertificateTag));
+ std::string base64_cert;
+ if (!base::Base64Encode(local_cert_, &base64_cert)) {
+ LOG(DFATAL) << "Cannot perform base64 encode on certificate";
+ }
+ certificate_tag->SetBodyText(base64_cert);
+ message->AddElement(certificate_tag);
+
+ state_ = ACCEPTED;
+ return message;
+}
+
+ChannelAuthenticator*
+V1HostAuthenticator::CreateChannelAuthenticator() const {
+ DCHECK_EQ(state_, ACCEPTED);
+ return new V1HostChannelAuthenticator(
+ local_cert_, local_private_key_, shared_secret_);
+};
+
+V1HostAuthenticatorFactory::V1HostAuthenticatorFactory(
+ const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& shared_secret)
+ : local_cert_(local_cert),
+ shared_secret_(shared_secret) {
+ DCHECK(local_private_key);
+
+ // TODO(hclam): Need a better way to clone a key. See crbug.com/105220 .
+ std::vector<uint8> key_bytes;
+ CHECK(local_private_key->ExportPrivateKey(&key_bytes));
+ local_private_key_.reset(
+ crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes));
+ CHECK(local_private_key_.get());
+}
+
+V1HostAuthenticatorFactory::~V1HostAuthenticatorFactory() {
+}
+
+Authenticator* V1HostAuthenticatorFactory::CreateAuthenticator(
+ const std::string& remote_jid,
+ const buzz::XmlElement* first_message) {
+ return new V1HostAuthenticator(local_cert_, local_private_key_.get(),
+ shared_secret_, remote_jid);
+}
+
+} // namespace remoting
+} // namespace protocol
diff --git a/remoting/protocol/v1_authenticator.h b/remoting/protocol/v1_authenticator.h
new file mode 100644
index 0000000..d53c5bf
--- /dev/null
+++ b/remoting/protocol/v1_authenticator.h
@@ -0,0 +1,89 @@
+// 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_AUTHENTICATOR_H_
+#define REMOTING_PROTOCOL_V1_AUTHENTICATOR_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "remoting/protocol/authenticator.h"
+
+namespace crypto {
+class RSAPrivateKey;
+} // namespace base
+
+namespace remoting {
+namespace protocol {
+
+class V1ClientAuthenticator : public Authenticator {
+ public:
+ explicit V1ClientAuthenticator(const std::string& local_jid,
+ const std::string& shared_secret);
+ virtual ~V1ClientAuthenticator();
+
+ // Authenticator interface.
+ virtual State state() const OVERRIDE;
+ virtual void ProcessMessage(const buzz::XmlElement* message) OVERRIDE;
+ virtual buzz::XmlElement* GetNextMessage() OVERRIDE;
+ virtual ChannelAuthenticator* CreateChannelAuthenticator() const OVERRIDE;
+
+ private:
+ std::string local_jid_;
+ std::string shared_secret_;
+ std::string remote_cert_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(V1ClientAuthenticator);
+};
+
+class V1HostAuthenticator : public Authenticator {
+ public:
+ explicit V1HostAuthenticator(const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& shared_secret,
+ const std::string& remote_jid);
+ virtual ~V1HostAuthenticator();
+
+ // Authenticator interface.
+ virtual State state() const OVERRIDE;
+ virtual void ProcessMessage(const buzz::XmlElement* message) OVERRIDE;
+ virtual buzz::XmlElement* GetNextMessage() OVERRIDE;
+ virtual ChannelAuthenticator* CreateChannelAuthenticator() const OVERRIDE;
+
+ private:
+ std::string local_cert_;
+ crypto::RSAPrivateKey* local_private_key_;
+ std::string shared_secret_;
+ std::string remote_jid_;
+ State state_;
+
+ DISALLOW_COPY_AND_ASSIGN(V1HostAuthenticator);
+};
+
+class V1HostAuthenticatorFactory : public AuthenticatorFactory {
+ public:
+ // Doesn't take ownership of |local_private_key|.
+ V1HostAuthenticatorFactory(const std::string& local_cert,
+ crypto::RSAPrivateKey* local_private_key,
+ const std::string& shared_secret);
+ virtual ~V1HostAuthenticatorFactory();
+
+ // AuthenticatorFactory interface.
+ virtual Authenticator* CreateAuthenticator(
+ const std::string& remote_jid,
+ const buzz::XmlElement* first_message) OVERRIDE;
+
+ private:
+ std::string local_cert_;
+ scoped_ptr<crypto::RSAPrivateKey> local_private_key_;
+ std::string shared_secret_;
+
+ DISALLOW_COPY_AND_ASSIGN(V1HostAuthenticatorFactory);
+};
+
+} // namespace protocol
+} // namespace remoting
+
+#endif // REMOTING_PROTOCOL_V1_AUTHENTICATOR_H_
diff --git a/remoting/protocol/v1_authenticator_unittest.cc b/remoting/protocol/v1_authenticator_unittest.cc
new file mode 100644
index 0000000..0ba9e51
--- /dev/null
+++ b/remoting/protocol/v1_authenticator_unittest.cc
@@ -0,0 +1,121 @@
+// 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 "base/file_path.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "crypto/rsa_private_key.h"
+#include "remoting/protocol/v1_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"
+
+namespace remoting {
+namespace protocol {
+
+namespace {
+const char kHostJid[] = "host1@gmail.com/123";
+const char kClientJid[] = "host2@gmail.com/321";
+
+const char kTestSharedSecret[] = "1234-1234-5678";
+const char kTestSharedSecretBad[] = "0000-0000-0001";
+} // namespace
+
+class V1AuthenticatorTest : public testing::Test {
+ public:
+ V1AuthenticatorTest() {
+ }
+ virtual ~V1AuthenticatorTest() {
+ }
+
+ protected:
+ void InitAuthenticators(const std::string& client_secret,
+ const std::string& host_secret) {
+ 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");
+ std::string cert_der;
+ ASSERT_TRUE(file_util::ReadFileToString(cert_path, &cert_der));
+
+ 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));
+
+ host_.reset(new V1HostAuthenticator(
+ cert_der, private_key_.get(), host_secret, kClientJid));
+ client_.reset(new V1ClientAuthenticator(kClientJid, client_secret));
+ }
+
+ void RunAuthExchange() {
+ do {
+ scoped_ptr<buzz::XmlElement> message;
+
+ // Pass message from client to host.
+ ASSERT_EQ(Authenticator::MESSAGE_READY, client_->state());
+ message.reset(client_->GetNextMessage());
+ ASSERT_TRUE(message.get());
+ ASSERT_NE(Authenticator::MESSAGE_READY, client_->state());
+
+ ASSERT_EQ(Authenticator::WAITING_MESSAGE, host_->state());
+ host_->ProcessMessage(message.get());
+ ASSERT_NE(Authenticator::WAITING_MESSAGE, host_->state());
+
+ // Are we done yet?
+ if (host_->state() == Authenticator::ACCEPTED ||
+ host_->state() == Authenticator::REJECTED) {
+ break;
+ }
+
+ // Pass message from host to client.
+ ASSERT_EQ(Authenticator::MESSAGE_READY, host_->state());
+ message.reset(host_->GetNextMessage());
+ ASSERT_TRUE(message.get());
+ ASSERT_NE(Authenticator::MESSAGE_READY, host_->state());
+
+ ASSERT_EQ(Authenticator::WAITING_MESSAGE, client_->state());
+ client_->ProcessMessage(message.get());
+ ASSERT_NE(Authenticator::WAITING_MESSAGE, client_->state());
+ } while (host_->state() != Authenticator::ACCEPTED &&
+ host_->state() != Authenticator::REJECTED);
+ }
+
+ scoped_ptr<crypto::RSAPrivateKey> private_key_;
+ scoped_ptr<V1HostAuthenticator> host_;
+ scoped_ptr<V1ClientAuthenticator> client_;
+
+ DISALLOW_COPY_AND_ASSIGN(V1AuthenticatorTest);
+};
+
+TEST_F(V1AuthenticatorTest, SuccessfulAuth) {
+ {
+ SCOPED_TRACE("RunAuthExchange");
+ InitAuthenticators(kTestSharedSecret, kTestSharedSecret);
+ RunAuthExchange();
+ }
+ ASSERT_EQ(Authenticator::ACCEPTED, host_->state());
+ ASSERT_EQ(Authenticator::ACCEPTED, client_->state());
+}
+
+TEST_F(V1AuthenticatorTest, InvalidSecret) {
+ {
+ SCOPED_TRACE("RunAuthExchange");
+ InitAuthenticators(kTestSharedSecretBad, kTestSharedSecret);
+ RunAuthExchange();
+ }
+ ASSERT_EQ(Authenticator::REJECTED, host_->state());
+}
+
+} // namespace protocol
+} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 0a4b8e3..de106f7 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -814,6 +814,8 @@
'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',
@@ -932,6 +934,7 @@
'protocol/protocol_mock_objects.h',
'protocol/rtp_video_reader_unittest.cc',
'protocol/rtp_video_writer_unittest.cc',
+ 'protocol/v1_authenticator_unittest.cc',
'run_all_unittests.cc',
],
'conditions': [