Commit bc407e96 authored by sergeyu@chromium.org's avatar sergeyu@chromium.org

Multi-step authentication support in JingleSession.

BUG=105214


Review URL: http://codereview.chromium.org/8774031

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114187 0039d316-1c4b-4281-b951-d872f2087c98
parent 76e68448
// 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/authenticator.h"
#include "remoting/base/constants.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
namespace remoting {
namespace protocol {
namespace {
const buzz::StaticQName kAuthenticationQName = { kChromotingXmlNamespace,
"authentication" };
} // namespace
// static
bool Authenticator::IsAuthenticatorMessage(const buzz::XmlElement* message) {
return message->Name() == kAuthenticationQName;
}
// static
buzz::XmlElement* Authenticator::CreateEmptyAuthenticatorMessage() {
return new buzz::XmlElement(kAuthenticationQName);
}
// static
const buzz::XmlElement* Authenticator::FindAuthenticatorMessage(
const buzz::XmlElement* message) {
return message->FirstNamed(kAuthenticationQName);
}
} // namespace protocol
} // namespace remoting
...@@ -53,6 +53,17 @@ class Authenticator { ...@@ -53,6 +53,17 @@ class Authenticator {
REJECTED, REJECTED,
}; };
// Returns true if |message| is an Authenticator message.
static bool IsAuthenticatorMessage(const buzz::XmlElement* message);
// Creates an empty Authenticator message, owned by the caller.
static buzz::XmlElement* CreateEmptyAuthenticatorMessage();
// Finds Authenticator message among child elements of |message|, or
// returns NULL otherwise.
static const buzz::XmlElement* FindAuthenticatorMessage(
const buzz::XmlElement* message);
Authenticator() {} Authenticator() {}
virtual ~Authenticator() {} virtual ~Authenticator() {}
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/string_number_conversions.h" #include "base/string_number_conversions.h"
#include "remoting/base/constants.h" #include "remoting/base/constants.h"
#include "remoting/protocol/authenticator.h"
#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h"
using buzz::QName; using buzz::QName;
...@@ -27,7 +28,6 @@ const char kControlTag[] = "control"; ...@@ -27,7 +28,6 @@ const char kControlTag[] = "control";
const char kEventTag[] = "event"; const char kEventTag[] = "event";
const char kVideoTag[] = "video"; const char kVideoTag[] = "video";
const char kResolutionTag[] = "initial-resolution"; const char kResolutionTag[] = "initial-resolution";
const char kAuthenticationTag[] = "authentication";
const char kTransportAttr[] = "transport"; const char kTransportAttr[] = "transport";
const char kVersionAttr[] = "version"; const char kVersionAttr[] = "version";
...@@ -198,8 +198,7 @@ XmlElement* ContentDescription::ToXml() const { ...@@ -198,8 +198,7 @@ XmlElement* ContentDescription::ToXml() const {
root->AddElement(resolution_tag); root->AddElement(resolution_tag);
if (authenticator_message_.get()) { if (authenticator_message_.get()) {
DCHECK(authenticator_message_->Name() == DCHECK(Authenticator::IsAuthenticatorMessage(authenticator_message_.get()));
QName(kChromotingXmlNamespace, kAuthenticationTag));
root->AddElement(new XmlElement(*authenticator_message_)); root->AddElement(new XmlElement(*authenticator_message_));
} }
...@@ -266,8 +265,7 @@ ContentDescription* ContentDescription::ParseXml( ...@@ -266,8 +265,7 @@ ContentDescription* ContentDescription::ParseXml(
*config->mutable_initial_resolution() = resolution; *config->mutable_initial_resolution() = resolution;
scoped_ptr<XmlElement> authenticator_message; scoped_ptr<XmlElement> authenticator_message;
child = element->FirstNamed(QName(kChromotingXmlNamespace, child = Authenticator::FindAuthenticatorMessage(element);
kAuthenticationTag));
if (child) if (child)
authenticator_message.reset(new XmlElement(*child)); authenticator_message.reset(new XmlElement(*child));
......
...@@ -45,6 +45,8 @@ JingleSession::JingleSession( ...@@ -45,6 +45,8 @@ JingleSession::JingleSession(
jid_ = cricket_session_->remote_name(); jid_ = cricket_session_->remote_name();
cricket_session_->SignalState.connect(this, &JingleSession::OnSessionState); cricket_session_->SignalState.connect(this, &JingleSession::OnSessionState);
cricket_session_->SignalError.connect(this, &JingleSession::OnSessionError); cricket_session_->SignalError.connect(this, &JingleSession::OnSessionError);
cricket_session_->SignalInfoMessage.connect(
this, &JingleSession::OnSessionInfoMessage);
cricket_session_->SignalReceivedTerminateReason.connect( cricket_session_->SignalReceivedTerminateReason.connect(
this, &JingleSession::OnTerminateReason); this, &JingleSession::OnTerminateReason);
} }
...@@ -246,6 +248,25 @@ void JingleSession::OnSessionError( ...@@ -246,6 +248,25 @@ void JingleSession::OnSessionError(
} }
} }
void JingleSession::OnSessionInfoMessage(cricket::Session* session,
const buzz::XmlElement* message) {
DCHECK_EQ(cricket_session_,session);
const buzz::XmlElement* auth_message =
Authenticator::FindAuthenticatorMessage(message);
if (auth_message) {
if (state_ != CONNECTED ||
authenticator_->state() != Authenticator::WAITING_MESSAGE) {
LOG(WARNING) << "Received unexpected authenticator message "
<< auth_message->Str();
return;
}
authenticator_->ProcessMessage(auth_message);
ProcessAuthenticationStep();
}
}
void JingleSession::OnTerminateReason(cricket::Session* session, void JingleSession::OnTerminateReason(cricket::Session* session,
const std::string& reason) { const std::string& reason) {
terminate_reason_ = reason; terminate_reason_ = reason;
...@@ -294,16 +315,10 @@ bool JingleSession::InitializeConfigFromDescription( ...@@ -294,16 +315,10 @@ bool JingleSession::InitializeConfigFromDescription(
return false; return false;
} }
DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); DCHECK_EQ(authenticator_->state(), Authenticator::WAITING_MESSAGE);
authenticator_->ProcessMessage(auth_message); authenticator_->ProcessMessage(auth_message);
// Support for more than two auth message is not implemented yet.
DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE &&
authenticator_->state() != Authenticator::MESSAGE_READY);
if (authenticator_->state() != Authenticator::ACCEPTED) {
return false;
}
// Initialize session configuration.
SessionConfig config; SessionConfig config;
if (!content_description->config()->GetFinalConfig(&config)) { if (!content_description->config()->GetFinalConfig(&config)) {
LOG(ERROR) << "Connection response does not specify configuration"; LOG(ERROR) << "Connection response does not specify configuration";
...@@ -333,8 +348,12 @@ void JingleSession::OnAccept() { ...@@ -333,8 +348,12 @@ void JingleSession::OnAccept() {
SetState(CONNECTED); SetState(CONNECTED);
if (authenticator_->state() == Authenticator::ACCEPTED) // Process authentication.
if (authenticator_->state() == Authenticator::ACCEPTED) {
SetState(AUTHENTICATED); SetState(AUTHENTICATED);
} else {
ProcessAuthenticationStep();
}
} }
void JingleSession::OnTerminate() { void JingleSession::OnTerminate() {
...@@ -398,8 +417,6 @@ void JingleSession::AcceptConnection() { ...@@ -398,8 +417,6 @@ void JingleSession::AcceptConnection() {
DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE);
authenticator_->ProcessMessage(auth_message); authenticator_->ProcessMessage(auth_message);
// Support for more than two auth message is not implemented yet.
DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE);
if (authenticator_->state() == Authenticator::REJECTED) { if (authenticator_->state() == Authenticator::REJECTED) {
CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED);
return; return;
...@@ -412,11 +429,29 @@ void JingleSession::AcceptConnection() { ...@@ -412,11 +429,29 @@ void JingleSession::AcceptConnection() {
buzz::XmlElement* auth_reply = NULL; buzz::XmlElement* auth_reply = NULL;
if (authenticator_->state() == Authenticator::MESSAGE_READY) if (authenticator_->state() == Authenticator::MESSAGE_READY)
auth_reply = authenticator_->GetNextMessage(); auth_reply = authenticator_->GetNextMessage();
DCHECK_EQ(authenticator_->state(), Authenticator::ACCEPTED); DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
cricket_session_->Accept( cricket_session_->Accept(
CreateSessionDescription(candidate_config, auth_reply)); CreateSessionDescription(candidate_config, auth_reply));
} }
void JingleSession::ProcessAuthenticationStep() {
DCHECK_EQ(state_, CONNECTED);
if (authenticator_->state() == Authenticator::MESSAGE_READY) {
buzz::XmlElement* auth_message = authenticator_->GetNextMessage();
cricket::XmlElements message;
message.push_back(auth_message);
cricket_session_->SendInfoMessage(message);
}
DCHECK_NE(authenticator_->state(), Authenticator::MESSAGE_READY);
if (authenticator_->state() == Authenticator::ACCEPTED) {
SetState(AUTHENTICATED);
} else if (authenticator_->state() == Authenticator::REJECTED) {
CloseInternal(net::ERR_CONNECTION_ABORTED, AUTHENTICATION_FAILED);
}
}
void JingleSession::AddChannelConnector( void JingleSession::AddChannelConnector(
const std::string& name, JingleChannelConnector* connector) { const std::string& name, JingleChannelConnector* connector) {
DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); DCHECK(channel_connectors_.find(name) == channel_connectors_.end());
......
...@@ -74,14 +74,15 @@ class JingleSession : public protocol::Session, ...@@ -74,14 +74,15 @@ class JingleSession : public protocol::Session,
bool InitializeConfigFromDescription( bool InitializeConfigFromDescription(
const cricket::SessionDescription* description); const cricket::SessionDescription* description);
// Used for Session.SignalState sigslot. // Handlers for |cricket_session_| signals.
void OnSessionState(cricket::BaseSession* session, void OnSessionState(cricket::BaseSession* session,
cricket::BaseSession::State state); cricket::BaseSession::State state);
// Used for Session.SignalError sigslot.
void OnSessionError(cricket::BaseSession* session, void OnSessionError(cricket::BaseSession* session,
cricket::BaseSession::Error error); cricket::BaseSession::Error error);
// Used for Session.SignalReceivedTerminateReason sigslot. void OnSessionInfoMessage(cricket::Session* session,
void OnTerminateReason(cricket::Session* session, const std::string& reason); const buzz::XmlElement* message);
void OnTerminateReason(cricket::Session* session,
const std::string& reason);
void OnInitiate(); void OnInitiate();
void OnAccept(); void OnAccept();
...@@ -91,6 +92,8 @@ class JingleSession : public protocol::Session, ...@@ -91,6 +92,8 @@ class JingleSession : public protocol::Session,
// accepts/rejects connection. // accepts/rejects connection.
void AcceptConnection(); void AcceptConnection();
void ProcessAuthenticationStep();
void AddChannelConnector(const std::string& name, void AddChannelConnector(const std::string& name,
JingleChannelConnector* connector); JingleChannelConnector* connector);
......
...@@ -171,14 +171,14 @@ class JingleSessionTest : public testing::Test { ...@@ -171,14 +171,14 @@ class JingleSessionTest : public testing::Test {
{ {
InSequence dummy; InSequence dummy;
EXPECT_CALL(host_connection_callback_,
OnStateChange(Session::CONNECTED))
.Times(AtMost(1));
if (expect_fail) { if (expect_fail) {
EXPECT_CALL(host_connection_callback_, EXPECT_CALL(host_connection_callback_,
OnStateChange(Session::FAILED)) OnStateChange(Session::FAILED))
.Times(1); .Times(1);
} else { } else {
EXPECT_CALL(host_connection_callback_,
OnStateChange(Session::CONNECTED))
.Times(1);
EXPECT_CALL(host_connection_callback_, EXPECT_CALL(host_connection_callback_,
OnStateChange(Session::AUTHENTICATED)) OnStateChange(Session::AUTHENTICATED))
.Times(1); .Times(1);
...@@ -191,14 +191,14 @@ class JingleSessionTest : public testing::Test { ...@@ -191,14 +191,14 @@ class JingleSessionTest : public testing::Test {
EXPECT_CALL(client_connection_callback_, EXPECT_CALL(client_connection_callback_,
OnStateChange(Session::CONNECTING)) OnStateChange(Session::CONNECTING))
.Times(1); .Times(1);
EXPECT_CALL(client_connection_callback_,
OnStateChange(Session::CONNECTED))
.Times(AtMost(1));
if (expect_fail) { if (expect_fail) {
EXPECT_CALL(client_connection_callback_, EXPECT_CALL(client_connection_callback_,
OnStateChange(Session::FAILED)) OnStateChange(Session::FAILED))
.Times(1); .Times(1);
} else { } else {
EXPECT_CALL(client_connection_callback_,
OnStateChange(Session::CONNECTED))
.Times(1);
EXPECT_CALL(client_connection_callback_, EXPECT_CALL(client_connection_callback_,
OnStateChange(Session::AUTHENTICATED)) OnStateChange(Session::AUTHENTICATED))
.Times(1); .Times(1);
...@@ -326,12 +326,24 @@ TEST_F(JingleSessionTest, Connect) { ...@@ -326,12 +326,24 @@ TEST_F(JingleSessionTest, Connect) {
InitiateConnection(1, FakeAuthenticator::ACCEPT, false); InitiateConnection(1, FakeAuthenticator::ACCEPT, false);
} }
// Verify that we can't connect two endpoints with mismatched secrets. // Verify that we can connect two endpoints with multi-step authentication.
TEST_F(JingleSessionTest, ConnectMultistep) {
CreateServerPair(3, FakeAuthenticator::ACCEPT);
InitiateConnection(3, FakeAuthenticator::ACCEPT, false);
}
// Verify that connection is terminated when auth fails.
TEST_F(JingleSessionTest, ConnectBadAuth) { TEST_F(JingleSessionTest, ConnectBadAuth) {
CreateServerPair(1, FakeAuthenticator::REJECT); CreateServerPair(1, FakeAuthenticator::REJECT);
InitiateConnection(1, FakeAuthenticator::ACCEPT, true); InitiateConnection(1, FakeAuthenticator::ACCEPT, true);
} }
// Verify that connection is terminted when multi-step auth fails.
TEST_F(JingleSessionTest, ConnectBadMultistepAuth) {
CreateServerPair(3, FakeAuthenticator::REJECT);
InitiateConnection(3, FakeAuthenticator::ACCEPT, true);
}
TEST_F(JingleSessionTest, ConnectBadChannelAuth) { TEST_F(JingleSessionTest, ConnectBadChannelAuth) {
CreateServerPair(1, FakeAuthenticator::REJECT_CHANNEL); CreateServerPair(1, FakeAuthenticator::REJECT_CHANNEL);
ASSERT_NO_FATAL_FAILURE( ASSERT_NO_FATAL_FAILURE(
...@@ -374,6 +386,21 @@ TEST_F(JingleSessionTest, TestTcpChannel) { ...@@ -374,6 +386,21 @@ TEST_F(JingleSessionTest, TestTcpChannel) {
tester.CheckResults(); tester.CheckResults();
} }
// Verify that we can connect channels with multistep auth.
TEST_F(JingleSessionTest, TestMultistepAuthTcpChannel) {
CreateServerPair(3, FakeAuthenticator::ACCEPT);
ASSERT_NO_FATAL_FAILURE(
InitiateConnection(3, FakeAuthenticator::ACCEPT, false));
ASSERT_NO_FATAL_FAILURE(CreateChannel());
StreamConnectionTester tester(host_socket_.get(), client_socket_.get(),
kMessageSize, kMessages);
tester.Start();
message_loop_.Run();
tester.CheckResults();
}
// Verify that data can be transmitted over the video RTP channel. // Verify that data can be transmitted over the video RTP channel.
TEST_F(JingleSessionTest, TestUdpChannel) { TEST_F(JingleSessionTest, TestUdpChannel) {
CreateServerPair(1, FakeAuthenticator::ACCEPT); CreateServerPair(1, FakeAuthenticator::ACCEPT);
......
...@@ -20,7 +20,6 @@ namespace remoting { ...@@ -20,7 +20,6 @@ namespace remoting {
namespace protocol { namespace protocol {
namespace { namespace {
const char kAuthenticationTag[] = "authentication";
const char kAuthTokenTag[] = "auth-token"; const char kAuthTokenTag[] = "auth-token";
const char kCertificateTag[] = "certificate"; const char kCertificateTag[] = "certificate";
} // namespace } // namespace
...@@ -64,19 +63,16 @@ void V1ClientAuthenticator::ProcessMessage(const XmlElement* message) { ...@@ -64,19 +63,16 @@ void V1ClientAuthenticator::ProcessMessage(const XmlElement* message) {
XmlElement* V1ClientAuthenticator::GetNextMessage() { XmlElement* V1ClientAuthenticator::GetNextMessage() {
DCHECK_EQ(state_, MESSAGE_READY); DCHECK_EQ(state_, MESSAGE_READY);
XmlElement* authentication_tag = new XmlElement( XmlElement* message = CreateEmptyAuthenticatorMessage();
QName(kChromotingXmlNamespace, kAuthenticationTag));
std::string token = std::string token =
protocol::GenerateSupportAuthToken(local_jid_, shared_secret_); protocol::GenerateSupportAuthToken(local_jid_, shared_secret_);
XmlElement* auth_token_tag = new XmlElement( XmlElement* auth_token_tag = new XmlElement(
QName(kChromotingXmlNamespace, kAuthTokenTag)); QName(kChromotingXmlNamespace, kAuthTokenTag));
auth_token_tag->SetBodyText(token); auth_token_tag->SetBodyText(token);
authentication_tag->AddElement(auth_token_tag); message->AddElement(auth_token_tag);
state_ = WAITING_MESSAGE; state_ = WAITING_MESSAGE;
return authentication_tag; return message;
} }
ChannelAuthenticator* ChannelAuthenticator*
...@@ -121,9 +117,7 @@ void V1HostAuthenticator::ProcessMessage(const XmlElement* message) { ...@@ -121,9 +117,7 @@ void V1HostAuthenticator::ProcessMessage(const XmlElement* message) {
XmlElement* V1HostAuthenticator::GetNextMessage() { XmlElement* V1HostAuthenticator::GetNextMessage() {
DCHECK_EQ(state_, MESSAGE_READY); DCHECK_EQ(state_, MESSAGE_READY);
XmlElement* message = new XmlElement( XmlElement* message = CreateEmptyAuthenticatorMessage();
QName(kChromotingXmlNamespace, kAuthenticationTag));
buzz::XmlElement* certificate_tag = new XmlElement( buzz::XmlElement* certificate_tag = new XmlElement(
buzz::QName(kChromotingXmlNamespace, kCertificateTag)); buzz::QName(kChromotingXmlNamespace, kCertificateTag));
std::string base64_cert; std::string base64_cert;
......
...@@ -743,6 +743,7 @@ ...@@ -743,6 +743,7 @@
'sources': [ 'sources': [
'protocol/auth_util.cc', 'protocol/auth_util.cc',
'protocol/auth_util.h', 'protocol/auth_util.h',
'protocol/authenticator.cc',
'protocol/authenticator.h', 'protocol/authenticator.h',
'protocol/buffered_socket_writer.cc', 'protocol/buffered_socket_writer.cc',
'protocol/buffered_socket_writer.h', 'protocol/buffered_socket_writer.h',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment