summaryrefslogtreecommitdiffstats
path: root/remoting/protocol/jingle_session.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/protocol/jingle_session.cc')
-rw-r--r--remoting/protocol/jingle_session.cc268
1 files changed, 209 insertions, 59 deletions
diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc
index f4acf8e..bf4b7d8 100644
--- a/remoting/protocol/jingle_session.cc
+++ b/remoting/protocol/jingle_session.cc
@@ -4,8 +4,16 @@
#include "remoting/protocol/jingle_session.h"
+#include "base/crypto/rsa_private_key.h"
#include "base/message_loop.h"
+#include "net/base/cert_verifier.h"
+#include "net/base/host_port_pair.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/base/constants.h"
#include "remoting/jingle_glue/channel_socket_adapter.h"
#include "remoting/jingle_glue/jingle_thread.h"
@@ -22,24 +30,77 @@ namespace remoting {
namespace protocol {
+const char JingleSession::kChromotingContentName[] = "chromoting";
+
namespace {
const char kControlChannelName[] = "control";
const char kEventChannelName[] = "event";
const char kVideoChannelName[] = "video";
const char kVideoRtpChannelName[] = "videortp";
const char kVideoRtcpChannelName[] = "videortcp";
+
+// Helper method to create a SSL client socket.
+net::SSLClientSocket* CreateSSLClientSocket(
+ net::ClientSocket* socket, scoped_refptr<net::X509Certificate> cert,
+ net::CertVerifier* cert_verifier) {
+ net::SSLConfig ssl_config;
+ ssl_config.false_start_enabled = false;
+ ssl_config.snap_start_enabled = false;
+ ssl_config.ssl3_enabled = true;
+ ssl_config.tls1_enabled = true;
+ ssl_config.session_resume_disabled = true;
+
+ // Certificate provided by the host doesn't need authority.
+ net::SSLConfig::CertAndStatus cert_and_status;
+ cert_and_status.cert_status = net::ERR_CERT_AUTHORITY_INVALID;
+ cert_and_status.cert = cert;
+ ssl_config.allowed_bad_certs.push_back(cert_and_status);
+
+ // SSLClientSocket takes ownership of the adapter.
+ net::HostPortPair host_and_pair(JingleSession::kChromotingContentName, 0);
+ net::SSLClientSocket* ssl_socket =
+ net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
+ socket, host_and_pair, ssl_config, NULL, cert_verifier);
+ return ssl_socket;
+}
+
} // namespace
-const char JingleSession::kChromotingContentName[] = "chromoting";
+// static
+JingleSession* JingleSession::CreateClientSession(
+ JingleSessionManager* manager,
+ scoped_refptr<net::X509Certificate> certificate) {
+ return new JingleSession(manager, certificate, NULL);
+}
+
+// static
+JingleSession* JingleSession::CreateServerSession(
+ JingleSessionManager* manager,
+ scoped_refptr<net::X509Certificate> certificate,
+ base::RSAPrivateKey* key) {
+ return new JingleSession(manager, certificate, key);
+}
JingleSession::JingleSession(
- JingleSessionManager* jingle_session_manager)
+ JingleSessionManager* jingle_session_manager,
+ scoped_refptr<net::X509Certificate> server_cert, base::RSAPrivateKey* key)
: jingle_session_manager_(jingle_session_manager),
+ server_cert_(server_cert),
state_(INITIALIZING),
closed_(false),
cricket_session_(NULL),
event_channel_(NULL),
- video_channel_(NULL) {
+ video_channel_(NULL),
+ ssl_connections_(0),
+ ALLOW_THIS_IN_INITIALIZER_LIST(connect_callback_(
+ NewCallback(this, &JingleSession::OnSSLConnect))) {
+ // TODO(hclam): Need a better way to clone a key.
+ if (key) {
+ std::vector<uint8> key_bytes;
+ CHECK(key->ExportPrivateKey(&key_bytes));
+ key_.reset(base::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes));
+ CHECK(key_.get());
+ }
}
JingleSession::~JingleSession() {
@@ -51,10 +112,73 @@ void JingleSession::Init(cricket::Session* cricket_session) {
cricket_session_ = cricket_session;
jid_ = cricket_session_->remote_name();
+ cert_verifier_.reset(new net::CertVerifier());
cricket_session_->SignalState.connect(
this, &JingleSession::OnSessionState);
}
+void JingleSession::CloseInternal(Task* closed_task, int result) {
+ if (MessageLoop::current() != jingle_session_manager_->message_loop()) {
+ jingle_session_manager_->message_loop()->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &JingleSession::CloseInternal,
+ closed_task, result));
+ return;
+ }
+
+ if (!closed_) {
+ if (control_ssl_socket_.get())
+ control_ssl_socket_.reset();
+
+ if (control_channel_adapter_.get())
+ control_channel_adapter_->Close(result);
+
+ if (control_channel_) {
+ control_channel_->OnSessionTerminate(cricket_session_);
+ control_channel_ = NULL;
+ }
+
+ if (event_ssl_socket_.get())
+ event_ssl_socket_.reset();
+
+ if (event_channel_adapter_.get())
+ event_channel_adapter_->Close(result);
+
+ if (event_channel_) {
+ event_channel_->OnSessionTerminate(cricket_session_);
+ event_channel_ = NULL;
+ }
+
+ if (video_ssl_socket_.get())
+ video_ssl_socket_.reset();
+
+ if (video_channel_adapter_.get())
+ video_channel_adapter_->Close(result);
+
+ if (video_channel_) {
+ video_channel_->OnSessionTerminate(cricket_session_);
+ video_channel_ = NULL;
+ }
+
+ if (video_rtp_channel_.get())
+ video_rtp_channel_->Close(result);
+ if (video_rtcp_channel_.get())
+ video_rtcp_channel_->Close(result);
+
+ if (cricket_session_)
+ cricket_session_->Terminate();
+
+ SetState(CLOSED);
+
+ closed_ = true;
+ }
+ cert_verifier_.reset();
+
+ if (closed_task) {
+ closed_task->Run();
+ delete closed_task;
+ }
+}
+
bool JingleSession::HasSession(cricket::Session* cricket_session) {
return cricket_session_ == cricket_session;
}
@@ -71,8 +195,7 @@ cricket::Session* JingleSession::ReleaseSession() {
return session;
}
-void JingleSession::SetStateChangeCallback(
- StateChangeCallback* callback) {
+void JingleSession::SetStateChangeCallback(StateChangeCallback* callback) {
DCHECK_EQ(jingle_session_manager_->message_loop(), MessageLoop::current());
DCHECK(callback);
state_change_callback_.reset(callback);
@@ -80,18 +203,18 @@ void JingleSession::SetStateChangeCallback(
net::Socket* JingleSession::control_channel() {
DCHECK_EQ(jingle_session_manager_->message_loop(), MessageLoop::current());
- return control_channel_adapter_.get();
+ return control_ssl_socket_.get();
}
net::Socket* JingleSession::event_channel() {
DCHECK_EQ(jingle_session_manager_->message_loop(), MessageLoop::current());
- return event_channel_adapter_.get();
+ return event_ssl_socket_.get();
}
// TODO(sergeyu): Remove this method after we switch to RTP.
net::Socket* JingleSession::video_channel() {
DCHECK_EQ(jingle_session_manager_->message_loop(), MessageLoop::current());
- return video_channel_adapter_.get();
+ return video_ssl_socket_.get();
}
net::Socket* JingleSession::video_rtp_channel() {
@@ -114,8 +237,7 @@ MessageLoop* JingleSession::message_loop() {
return jingle_session_manager_->message_loop();
}
-const CandidateSessionConfig*
-JingleSession::candidate_config() {
+const CandidateSessionConfig* JingleSession::candidate_config() {
DCHECK(candidate_config_.get());
return candidate_config_.get();
}
@@ -155,53 +277,7 @@ void JingleSession::set_receiver_token(const std::string& receiver_token) {
}
void JingleSession::Close(Task* closed_task) {
- if (MessageLoop::current() != jingle_session_manager_->message_loop()) {
- jingle_session_manager_->message_loop()->PostTask(
- FROM_HERE, NewRunnableMethod(this, &JingleSession::Close,
- closed_task));
- return;
- }
-
- if (!closed_) {
- if (control_channel_adapter_.get())
- control_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED);
-
- if (control_channel_) {
- control_channel_->OnSessionTerminate(cricket_session_);
- control_channel_ = NULL;
- }
-
- if (event_channel_adapter_.get())
- event_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED);
-
- if (event_channel_) {
- event_channel_->OnSessionTerminate(cricket_session_);
- event_channel_ = NULL;
- }
-
- if (video_channel_adapter_.get())
- video_channel_adapter_->Close(net::ERR_CONNECTION_CLOSED);
-
- if (video_channel_) {
- video_channel_->OnSessionTerminate(cricket_session_);
- video_channel_ = NULL;
- }
-
- if (video_rtp_channel_.get())
- video_rtp_channel_->Close(net::ERR_CONNECTION_CLOSED);
- if (video_rtcp_channel_.get())
- video_rtcp_channel_->Close(net::ERR_CONNECTION_CLOSED);
-
- if (cricket_session_)
- cricket_session_->Terminate();
-
- SetState(CLOSED);
-
- closed_ = true;
- }
-
- closed_task->Run();
- delete closed_task;
+ CloseInternal(closed_task, net::ERR_CONNECTION_CLOSED);
}
void JingleSession::OnSessionState(
@@ -287,7 +363,48 @@ void JingleSession::OnInitiate() {
SetState(CONNECTING);
}
+bool JingleSession::EstablishSSLConnection(
+ net::ClientSocket* adapter, scoped_ptr<net::Socket>* ssl_socket) {
+ if (cricket_session_->initiator()) {
+ // Create client SSL socket.
+ net::SSLClientSocket* socket = CreateSSLClientSocket(adapter,
+ server_cert_,
+ cert_verifier_.get());
+ ssl_socket->reset(socket);
+
+ int ret = socket->Connect(connect_callback_.get());
+ if (ret == net::ERR_IO_PENDING) {
+ return true;
+ } else if (ret != net::OK) {
+ LOG(ERROR) << "Failed to establish SSL connection";
+ cricket_session_->Terminate();
+ return false;
+ }
+ } else {
+ // Create server SSL socket.
+ net::SSLConfig ssl_config;
+ net::SSLServerSocket* socket = net::CreateSSLServerSocket(
+ adapter, server_cert_, key_.get(), ssl_config);
+ ssl_socket->reset(socket);
+
+ int ret = socket->Accept(connect_callback_.get());
+ if (ret == net::ERR_IO_PENDING) {
+ return true;
+ } else if (ret != net::OK) {
+ LOG(ERROR) << "Failed to establish SSL connection";
+ cricket_session_->Terminate();
+ return true;
+ }
+ }
+ // Reach here if net::OK is received.
+ connect_callback_->Run(net::OK);
+ return true;
+}
+
void JingleSession::OnAccept() {
+ // TODO(hclam): Need to close the adapters on failuire otherwise it will
+ // crash in the destructor.
+
// Set the config if we are the one who initiated the session.
if (cricket_session_->initiator()) {
const cricket::ContentInfo* content =
@@ -307,11 +424,24 @@ void JingleSession::OnAccept() {
cricket_session_->Terminate();
return;
}
-
set_config(config);
}
- SetState(CONNECTED);
+ bool ret = EstablishSSLConnection(control_channel_adapter_.release(),
+ &control_ssl_socket_);
+ if (ret) {
+ ret = EstablishSSLConnection(event_channel_adapter_.release(),
+ &event_ssl_socket_);
+ }
+ if (ret) {
+ ret = EstablishSSLConnection(video_channel_adapter_.release(),
+ &video_ssl_socket_);
+ }
+
+ if (!ret) {
+ LOG(ERROR) << "Failed to establish SSL connections";
+ cricket_session_->Terminate();
+ }
}
void JingleSession::OnTerminate() {
@@ -346,6 +476,26 @@ void JingleSession::OnTerminate() {
closed_ = true;
}
+void JingleSession::OnSSLConnect(int result) {
+ DCHECK(!closed_);
+ if (result != net::OK) {
+ LOG(ERROR) << "Error during SSL connection: " << result;
+ // TODO(hclam): Just setting the state is not enough. Need to invoke a
+ // callback to report failure.
+ CloseInternal(NULL, result);
+ return;
+ }
+
+ // Number of channels for a jingle session.
+ const int kChannels = 3;
+
+ // Set the state to connected only of all SSL sockets are connected.
+ if (++ssl_connections_ == kChannels) {
+ SetState(CONNECTED);
+ }
+ CHECK(ssl_connections_ <= kChannels) << "Unexpected SSL connect callback";
+}
+
void JingleSession::SetState(State new_state) {
if (new_state != state_) {
state_ = new_state;