diff options
author | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-04 06:26:01 +0000 |
---|---|---|
committer | jamiewalch@chromium.org <jamiewalch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-04 06:26:01 +0000 |
commit | 8835692aa95097e2df4a2ec2b2db4768578b06a0 (patch) | |
tree | 3eb40c08525740259ff976b617befdacad442541 /remoting | |
parent | de1bb9caab81d7a80dac9ebe59a5ea2aa56048dd (diff) | |
download | chromium_src-8835692aa95097e2df4a2ec2b2db4768578b06a0.zip chromium_src-8835692aa95097e2df4a2ec2b2db4768578b06a0.tar.gz chromium_src-8835692aa95097e2df4a2ec2b2db4768578b06a0.tar.bz2 |
This CL adds the "request pairing" implementation between the web-app and the host. Specifically:
* Adds a pairing registry to the Chromoting host.
* Checks the state of the "remember me" checkbox and sends a pairing request if needed.
* Adds the plumbing to get that request to host, and to get the response back to the web-app.
* Saves the pairing response to local storage, and uses it next time we connect.
* Uses Base64 throughout for the secret, since unencoded random strings can't be serialized via PPAPI.
Note that pairing is still disabled because we're still missing per-platform Delegate implementations, a UI for revoking pairings, and policy support for disabling the feature.
BUG=156182
Review URL: https://chromiumcodereview.appspot.com/16137004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203865 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
23 files changed, 284 insertions, 48 deletions
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc index 4411adf..013060c 100644 --- a/remoting/client/chromoting_client.cc +++ b/remoting/client/chromoting_client.cc @@ -55,13 +55,12 @@ void ChromotingClient::Start( scoped_ptr<protocol::TransportFactory> transport_factory) { DCHECK(task_runner_->BelongsToCurrentThread()); - // TODO(jamiewalch): Add the plumbing required to get the client id and - // shared secret from the web-app. - std::string client_id, shared_secret; scoped_ptr<protocol::Authenticator> authenticator( new protocol::NegotiatingClientAuthenticator( - client_id, shared_secret, - config_.authentication_tag, config_.fetch_secret_callback, + config_.client_pairing_id, + config_.client_paired_secret, + config_.authentication_tag, + config_.fetch_secret_callback, user_interface_->GetTokenFetcher(config_.host_public_key), config_.authentication_methods)); @@ -123,6 +122,13 @@ void ChromotingClient::SetCapabilities( IntersectCapabilities(config_.capabilities, host_capabilities_)); } +void ChromotingClient::SetPairingResponse( + const protocol::PairingResponse& pairing_response) { + DCHECK(task_runner_->BelongsToCurrentThread()); + + user_interface_->SetPairingResponse(pairing_response); +} + void ChromotingClient::InjectClipboardEvent( const protocol::ClipboardEvent& event) { DCHECK(task_runner_->BelongsToCurrentThread()); diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h index d46743c..eb044be 100644 --- a/remoting/client/chromoting_client.h +++ b/remoting/client/chromoting_client.h @@ -64,6 +64,8 @@ class ChromotingClient : public protocol::ConnectionToHost::HostEventCallback, // ClientStub implementation. virtual void SetCapabilities( const protocol::Capabilities& capabilities) OVERRIDE; + virtual void SetPairingResponse( + const protocol::PairingResponse& pairing_response) OVERRIDE; // ClipboardStub implementation for receiving clipboard data from host. virtual void InjectClipboardEvent( diff --git a/remoting/client/client_config.h b/remoting/client/client_config.h index 35f7286..7270abd 100644 --- a/remoting/client/client_config.h +++ b/remoting/client/client_config.h @@ -30,6 +30,14 @@ struct ClientConfig { // The set of all capabilities supported by the webapp. std::string capabilities; + + // The host-generated id and secret for paired clients. Paired clients + // should set both of these in addition to fetch_secret_callback; the + // latter is used if the paired connection fails (for example, if the + // pairing has been revoked by the host) and the user needs to prompted + // to enter their PIN. + std::string client_pairing_id; + std::string client_paired_secret; }; } // namespace remoting diff --git a/remoting/client/client_user_interface.h b/remoting/client/client_user_interface.h index 59460f1..9779835 100644 --- a/remoting/client/client_user_interface.h +++ b/remoting/client/client_user_interface.h @@ -17,6 +17,7 @@ namespace remoting { namespace protocol { class ClipboardStub; class CursorShapeStub; +class PairingResponse; } // namespace protocol // ClientUserInterface is an interface that must be implemented by @@ -37,6 +38,10 @@ class ClientUserInterface { // to the application. virtual void SetCapabilities(const std::string& capabilities) = 0; + // Passes a pairing response message to the client. + virtual void SetPairingResponse( + const protocol::PairingResponse& pairing_response) = 0; + // Get the view's ClipboardStub implementation. virtual protocol::ClipboardStub* GetClipboardStub() = 0; diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index 3d420ba..8b775c1 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -141,7 +141,7 @@ logging::LogMessageHandlerFunction g_logging_old_handler = NULL; const char ChromotingInstance::kApiFeatures[] = "highQualityScaling injectKeyEvent sendClipboardItem remapKey trapKey " "notifyClientDimensions notifyClientResolution pauseVideo pauseAudio " - "asyncPin thirdPartyAuth"; + "asyncPin thirdPartyAuth pinlessAuth"; const char ChromotingInstance::kRequestedCapabilities[] = ""; const char ChromotingInstance::kSupportedCapabilities[] = ""; @@ -289,6 +289,8 @@ void ChromotingInstance::HandleMessage(const pp::Var& message) { LOG(ERROR) << "Invalid connect() data."; return; } + data->GetString("clientPairingId", &config.client_pairing_id); + data->GetString("clientPairedSecret", &config.client_paired_secret); if (use_async_pin_dialog_) { config.fetch_secret_callback = base::Bind(&ChromotingInstance::FetchSecretFromDialog, @@ -424,6 +426,13 @@ void ChromotingInstance::HandleMessage(const pp::Var& message) { return; } OnThirdPartyTokenFetched(token, shared_secret); + } else if (method == "requestPairing") { + std::string client_name; + if (!data->GetString("clientName", &client_name)) { + LOG(ERROR) << "Invalid requestPairing"; + return; + } + RequestPairing(client_name); } } @@ -504,6 +513,14 @@ void ChromotingInstance::SetCapabilities(const std::string& capabilities) { PostChromotingMessage("setCapabilities", data.Pass()); } +void ChromotingInstance::SetPairingResponse( + const protocol::PairingResponse& pairing_response) { + scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); + data->SetString("clientId", pairing_response.client_id()); + data->SetString("sharedSecret", pairing_response.shared_secret()); + PostChromotingMessage("pairingResponse", data.Pass()); +} + void ChromotingInstance::FetchSecretFromDialog( bool pairing_supported, const protocol::SecretFetchedCallback& secret_fetched_callback) { @@ -779,6 +796,15 @@ void ChromotingInstance::OnThirdPartyTokenFetched( } } +void ChromotingInstance::RequestPairing(const std::string& client_name) { + if (!IsConnected()) { + return; + } + protocol::PairingRequest pairing_request; + pairing_request.set_client_name(client_name); + host_connection_->host_stub()->RequestPairing(pairing_request); +} + ChromotingStats* ChromotingInstance::GetStats() { if (!client_.get()) return NULL; diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index d90f86d..c71576b 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -119,6 +119,8 @@ class ChromotingInstance : protocol::ErrorCode error) OVERRIDE; virtual void OnConnectionReady(bool ready) OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; + virtual void SetPairingResponse( + const protocol::PairingResponse& pairing_response) OVERRIDE; virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE; virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE; virtual scoped_ptr<protocol::ThirdPartyClientAuthenticator::TokenFetcher> @@ -195,6 +197,7 @@ class ChromotingInstance : void OnPinFetched(const std::string& pin); void OnThirdPartyTokenFetched(const std::string& token, const std::string& shared_secret); + void RequestPairing(const std::string& client_name); // Helper method to post messages to the webapp. void PostChromotingMessage(const std::string& method, diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index d090e05..fe754ed 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -298,7 +298,8 @@ void ChromotingHost::OnIncomingSession( ui_task_runner_, connection.Pass(), desktop_environment_factory_, - max_session_duration_); + max_session_duration_, + pairing_registry_); clients_.push_back(client); } diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h index d72e99d..a0dc3d0 100644 --- a/remoting/host/chromoting_host.h +++ b/remoting/host/chromoting_host.h @@ -19,8 +19,9 @@ #include "remoting/host/host_status_monitor.h" #include "remoting/host/host_status_observer.h" #include "remoting/protocol/authenticator.h" -#include "remoting/protocol/session_manager.h" #include "remoting/protocol/connection_to_client.h" +#include "remoting/protocol/pairing_registry.h" +#include "remoting/protocol/session_manager.h" #include "third_party/skia/include/core/SkSize.h" namespace base { @@ -139,6 +140,16 @@ class ChromotingHost : public base::NonThreadSafe, return weak_factory_.GetWeakPtr(); } + // The host uses a pairing registry to generate and store pairing information + // for clients for PIN-less authentication. + scoped_refptr<protocol::PairingRegistry> pairing_registry() const { + return pairing_registry_; + } + void set_pairing_registry( + scoped_refptr<protocol::PairingRegistry> pairing_registry) { + pairing_registry_ = pairing_registry; + } + private: friend class ChromotingHostTest; @@ -190,6 +201,9 @@ class ChromotingHost : public base::NonThreadSafe, // The maximum duration of any session. base::TimeDelta max_session_duration_; + // The pairing registry for PIN-less authentication. + scoped_refptr<protocol::PairingRegistry> pairing_registry_; + base::WeakPtrFactory<ChromotingHost> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ChromotingHost); diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc index d7fce86..f7b56e2 100644 --- a/remoting/host/chromoting_host_unittest.cc +++ b/remoting/host/chromoting_host_unittest.cc @@ -191,7 +191,8 @@ class ChromotingHostTest : public testing::Test { task_runner_, // UI connection.Pass(), desktop_environment_factory_.get(), - base::TimeDelta())); + base::TimeDelta(), + NULL)); connection_ptr->set_host_stub(client.get()); diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index 2f17ce8..f4b72c7 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -27,6 +27,7 @@ #include "remoting/proto/event.pb.h" #include "remoting/protocol/client_stub.h" #include "remoting/protocol/clipboard_thread_proxy.h" +#include "remoting/protocol/pairing_registry.h" // Default DPI to assume for old clients that use notifyClientDimensions. const int kDefaultDPI = 96; @@ -43,7 +44,8 @@ ClientSession::ClientSession( scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, scoped_ptr<protocol::ConnectionToClient> connection, DesktopEnvironmentFactory* desktop_environment_factory, - const base::TimeDelta& max_duration) + const base::TimeDelta& max_duration, + scoped_refptr<protocol::PairingRegistry> pairing_registry) : event_handler_(event_handler), connection_(connection.Pass()), client_jid_(connection_->session()->jid()), @@ -63,7 +65,8 @@ ClientSession::ClientSession( video_capture_task_runner_(video_capture_task_runner), video_encode_task_runner_(video_encode_task_runner), network_task_runner_(network_task_runner), - ui_task_runner_(ui_task_runner) { + ui_task_runner_(ui_task_runner), + pairing_registry_(pairing_registry) { connection_->SetEventHandler(this); // TODO(sergeyu): Currently ConnectionToClient expects stubs to be @@ -172,6 +175,18 @@ void ClientSession::SetCapabilities( IntersectCapabilities(*client_capabilities_, host_capabilities_)); } +void ClientSession::RequestPairing( + const protocol::PairingRequest& pairing_request) { + if (pairing_request.has_client_name()) { + protocol::PairingRegistry::Pairing pairing = + pairing_registry_->CreatePairing(pairing_request.client_name()); + protocol::PairingResponse pairing_response; + pairing_response.set_client_id(pairing.client_id); + pairing_response.set_shared_secret(pairing.shared_secret); + connection_->client_stub()->SetPairingResponse(pairing_response); + } +} + void ClientSession::OnConnectionAuthenticated( protocol::ConnectionToClient* connection) { DCHECK(CalledOnValidThread()); diff --git a/remoting/host/client_session.h b/remoting/host/client_session.h index add2a0a..0c29029 100644 --- a/remoting/host/client_session.h +++ b/remoting/host/client_session.h @@ -24,6 +24,7 @@ #include "remoting/protocol/input_event_tracker.h" #include "remoting/protocol/input_filter.h" #include "remoting/protocol/input_stub.h" +#include "remoting/protocol/pairing_registry.h" #include "third_party/skia/include/core/SkPoint.h" #include "third_party/skia/include/core/SkSize.h" @@ -95,7 +96,8 @@ class ClientSession scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, scoped_ptr<protocol::ConnectionToClient> connection, DesktopEnvironmentFactory* desktop_environment_factory, - const base::TimeDelta& max_duration); + const base::TimeDelta& max_duration, + scoped_refptr<protocol::PairingRegistry> pairing_registry); virtual ~ClientSession(); // protocol::HostStub interface. @@ -107,6 +109,8 @@ class ClientSession const protocol::AudioControl& audio_control) OVERRIDE; virtual void SetCapabilities( const protocol::Capabilities& capabilities) OVERRIDE; + virtual void RequestPairing( + const remoting::protocol::PairingRequest& pairing_request) OVERRIDE; // protocol::ConnectionToClient::EventHandler interface. virtual void OnConnectionAuthenticated( @@ -224,6 +228,9 @@ class ClientSession // Used to apply client-requested changes in screen resolution. scoped_ptr<ScreenControls> screen_controls_; + // The pairing registry for PIN-less authentication. + scoped_refptr<protocol::PairingRegistry> pairing_registry_; + DISALLOW_COPY_AND_ASSIGN(ClientSession); }; diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc index 314c716..4b55caf 100644 --- a/remoting/host/client_session_unittest.cc +++ b/remoting/host/client_session_unittest.cc @@ -164,7 +164,8 @@ void ClientSessionTest::SetUp() { ui_task_runner, // UI thread. connection.PassAs<protocol::ConnectionToClient>(), desktop_environment_factory_.get(), - base::TimeDelta())); + base::TimeDelta(), + NULL)); } void ClientSessionTest::TearDown() { diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 1ac47cc..38765df 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -19,7 +19,6 @@ #include "base/single_thread_task_runner.h" #include "base/string_number_conversions.h" #include "base/string_util.h" -#include "base/string_util.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "base/utf_string_conversions.h" @@ -462,12 +461,19 @@ void HostProcess::CreateAuthenticatorFactory() { ShutdownHost(kInitializationFailed); return; } + + // TODO(jamiewalch): Add a pairing registry here once all the code + // is committed. + scoped_refptr<remoting::protocol::PairingRegistry> pairing_registry; + //scoped_refptr<protocol::PairingRegistry> pairing_registry( + // new protocol::PairingRegistry( + // scoped_ptr<protocol::PairingRegistry::Delegate>( + // new protocol::NotImplementedPairingRegistryDelegate), + // protocol::PairingRegistry::PairedClients())); + scoped_ptr<protocol::AuthenticatorFactory> factory; if (token_url_.is_empty() && token_validation_url_.is_empty()) { - // TODO(jamiewalch): Add a pairing registry here once all the code - // is committed. - scoped_refptr<remoting::protocol::PairingRegistry> pairing_registry; factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret( local_certificate, key_pair_, host_secret_hash_, pairing_registry); @@ -495,6 +501,8 @@ void HostProcess::CreateAuthenticatorFactory() { factory.reset(new PamAuthorizationFactory(factory.Pass())); #endif host_->SetAuthenticatorFactory(factory.Pass()); + + host_->set_pairing_registry(pairing_registry); } // IPC::Listener implementation. diff --git a/remoting/protocol/client_stub.h b/remoting/protocol/client_stub.h index 09a83c5..4507ba7 100644 --- a/remoting/protocol/client_stub.h +++ b/remoting/protocol/client_stub.h @@ -30,9 +30,7 @@ class ClientStub : public ClipboardStub, virtual void SetCapabilities(const Capabilities& capabilities) = 0; // Passes a pairing response message to the client. - // TODO(jamiewalch): Make this pure virtual once the PIN-less authentication - // implementation CLs have landed. - virtual void SetPairingResponse(const PairingResponse& pairing_response) {} + virtual void SetPairingResponse(const PairingResponse& pairing_response) = 0; private: DISALLOW_COPY_AND_ASSIGN(ClientStub); diff --git a/remoting/protocol/host_stub.h b/remoting/protocol/host_stub.h index d7d5475..46d7534 100644 --- a/remoting/protocol/host_stub.h +++ b/remoting/protocol/host_stub.h @@ -41,9 +41,7 @@ class HostStub { virtual void SetCapabilities(const Capabilities& capabilities) = 0; // Requests pairing between the host and client for PIN-less authentication. - // TODO(jamiewalch): Make this pure virtual once the PIN-less authentication - // implementation CLs have landed. - virtual void RequestPairing(const PairingRequest& pairing_request) {} + virtual void RequestPairing(const PairingRequest& pairing_request) = 0; protected: virtual ~HostStub() {} diff --git a/remoting/protocol/negotiating_authenticator_unittest.cc b/remoting/protocol/negotiating_authenticator_unittest.cc index 9cc2ebc..9b5bb8b 100644 --- a/remoting/protocol/negotiating_authenticator_unittest.cc +++ b/remoting/protocol/negotiating_authenticator_unittest.cc @@ -11,6 +11,7 @@ #include "remoting/protocol/negotiating_authenticator_base.h" #include "remoting/protocol/negotiating_client_authenticator.h" #include "remoting/protocol/negotiating_host_authenticator.h" +#include "remoting/protocol/pairing_registry.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" diff --git a/remoting/protocol/pairing_registry.cc b/remoting/protocol/pairing_registry.cc index 583d84b..54b66b0 100644 --- a/remoting/protocol/pairing_registry.cc +++ b/remoting/protocol/pairing_registry.cc @@ -36,7 +36,10 @@ const PairingRegistry::Pairing& PairingRegistry::CreatePairing( // Create a random shared secret to authenticate this client. char buffer[kKeySize]; crypto::RandBytes(buffer, arraysize(buffer)); - result.shared_secret = std::string(buffer, buffer+arraysize(buffer)); + if (!base::Base64Encode(base::StringPiece(buffer, arraysize(buffer)), + &result.shared_secret)) { + LOG(FATAL) << "Base64Encode failed."; + } // Save the result via the Delegate and return it to the caller. paired_clients_[result.client_id] = result; @@ -59,7 +62,7 @@ std::string PairingRegistry::GetSecret(const std::string& client_id) const { void NotImplementedPairingRegistryDelegate::Save( const PairingRegistry::PairedClients& paired_clients) { NOTIMPLEMENTED(); -}; +} } // namespace protocol } // namespace remoting diff --git a/remoting/protocol/pairing_registry.h b/remoting/protocol/pairing_registry.h index 4889356..f96097b 100644 --- a/remoting/protocol/pairing_registry.h +++ b/remoting/protocol/pairing_registry.h @@ -61,10 +61,6 @@ class PairingRegistry : public base::RefCountedThreadSafe<PairingRegistry>, virtual ~PairingRegistry(); - // Callback for the Delegate::Load method. Invoked when the stored pairings - // have been loaded. - void OnLoad(const PairedClients& paired_clients); - scoped_ptr<Delegate> delegate_; PairedClients paired_clients_; diff --git a/remoting/webapp/client_plugin.js b/remoting/webapp/client_plugin.js index 2b28336..e98b345 100644 --- a/remoting/webapp/client_plugin.js +++ b/remoting/webapp/client_plugin.js @@ -66,7 +66,8 @@ remoting.ClientPlugin.Feature = { REMAP_KEY: 'remapKey', SEND_CLIPBOARD_ITEM: 'sendClipboardItem', THIRD_PARTY_AUTH: 'thirdPartyAuth', - TRAP_KEY: 'trapKey' + TRAP_KEY: 'trapKey', + PINLESS_AUTH: 'pinlessAuth' }; /** @@ -102,10 +103,15 @@ remoting.ClientPlugin.prototype.onIncomingIq = function(iq) {}; * authentication methods the client should attempt to use. * @param {string} authenticationTag A host-specific tag to mix into * authentication hashes. + * @param {string} clientPairingId For paired Me2Me connections, the + * pairing id for this client, as issued by the host. + * @param {string} clientPairedSecret For paired Me2Me connections, the + * paired secret for this client, as issued by the host. */ remoting.ClientPlugin.prototype.connect = function( hostJid, hostPublicKey, localJid, sharedSecret, - authenticationMethods, authenticationTag) {}; + authenticationMethods, authenticationTag, + clientPairingId, clientPairedSecret) {}; /** * Release all currently pressed keys. @@ -199,3 +205,13 @@ remoting.ClientPlugin.prototype.useAsyncPinDialog = function() {}; */ remoting.ClientPlugin.prototype.onThirdPartyTokenFetched = function(token, sharedSecret) {}; + +/** + * Request pairing with the host for PIN-less authentication. + * + * @param {string} clientName The human-readable name of the client. + * @param {function(string, string):void} onDone, Callback to receive the + * client id and shared secret when they are available. + */ +remoting.ClientPlugin.prototype.requestPairing = function( + clientName, onDone) {}; diff --git a/remoting/webapp/client_plugin_async.js b/remoting/webapp/client_plugin_async.js index cb64329..8b60f30 100644 --- a/remoting/webapp/client_plugin_async.js +++ b/remoting/webapp/client_plugin_async.js @@ -64,7 +64,8 @@ remoting.ClientPluginAsync = function(plugin) { this.helloReceived_ = false; /** @type {function(boolean)|null} */ this.onInitializedCallback_ = null; - + /** @type {function(string, string):void} */ + this.onPairingComplete_ = function(clientId, sharedSecret) {}; /** @type {remoting.ClientSession.PerfStats} */ this.perfStats_ = new remoting.ClientSession.PerfStats(); @@ -298,6 +299,14 @@ remoting.ClientPluginAsync.prototype.handleMessage_ = function(messageStr) { /** @type {string} */ message.data['hostPublicKey']; var scope = /** @type {string} */ message.data['scope']; this.fetchThirdPartyTokenHandler(tokenUrl, hostPublicKey, scope); + } else if (message.method == 'pairingResponse') { + var clientId = /** @type {string} */ message.data['clientId']; + var sharedSecret = /** @type {string} */ message.data['sharedSecret']; + if (typeof clientId != 'string' || typeof sharedSecret != 'string') { + console.error('Received incorrect pairingResponse message.'); + return; + } + this.onPairingComplete_(clientId, sharedSecret); } }; @@ -385,10 +394,15 @@ remoting.ClientPluginAsync.prototype.onIncomingIq = function(iq) { * authentication methods the client should attempt to use. * @param {string} authenticationTag A host-specific tag to mix into * authentication hashes. + * @param {string} clientPairingId For paired Me2Me connections, the + * pairing id for this client, as issued by the host. + * @param {string} clientPairedSecret For paired Me2Me connections, the + * paired secret for this client, as issued by the host. */ remoting.ClientPluginAsync.prototype.connect = function( hostJid, hostPublicKey, localJid, sharedSecret, - authenticationMethods, authenticationTag) { + authenticationMethods, authenticationTag, + clientPairingId, clientPairedSecret) { this.plugin.postMessage(JSON.stringify( { method: 'connect', data: { hostJid: hostJid, @@ -397,7 +411,9 @@ remoting.ClientPluginAsync.prototype.connect = function( sharedSecret: sharedSecret, authenticationMethods: authenticationMethods, authenticationTag: authenticationTag, - capabilities: this.capabilities_.join(" ") + capabilities: this.capabilities_.join(" "), + clientPairingId: clientPairingId, + clientPairedSecret: clientPairedSecret } })); }; @@ -568,6 +584,23 @@ remoting.ClientPluginAsync.prototype.onThirdPartyTokenFetched = function( }; /** + * Request pairing with the host for PIN-less authentication. + * + * @param {string} clientName The human-readable name of the client. + * @param {function(string, string):void} onDone, Callback to receive the + * client id and shared secret when they are available. + */ +remoting.ClientPluginAsync.prototype.requestPairing = + function(clientName, onDone) { + if (!this.hasFeature(remoting.ClientPlugin.Feature.PINLESS_AUTH)) { + return; + } + this.onPairingComplete_ = onDone; + this.plugin.postMessage(JSON.stringify( + { method: 'requestPairing', data: { clientName: clientName } })); +}; + +/** * If we haven't yet received a "hello" message from the plugin, change its * size so that the user can confirm it if click-to-play is enabled, or can * see the "this plugin is disabled" message if it is actually disabled. diff --git a/remoting/webapp/client_screen.js b/remoting/webapp/client_screen.js index feaf29d..6cc70de 100644 --- a/remoting/webapp/client_screen.js +++ b/remoting/webapp/client_screen.js @@ -274,6 +274,10 @@ remoting.connectMe2MeHostVersionAcknowledged_ = function(host) { var pinForm = document.getElementById('pin-form'); /** @type {Element} */ var pinCancel = document.getElementById('cancel-pin-entry-button'); + /** @type {Element} */ + var rememberPin = document.getElementById('remember-pin'); + /** @type {Element} */ + var rememberPinCheckbox = document.getElementById('remember-pin-checkbox'); /** * Event handler for both the 'submit' and 'cancel' actions. Using * a single handler for both greatly simplifies the task of making @@ -292,23 +296,38 @@ remoting.connectMe2MeHostVersionAcknowledged_ = function(host) { event.preventDefault(); remoting.setMode(remoting.AppMode.CLIENT_CONNECTING); onPinFetched(pin); + if (/** @type {boolean} */(rememberPinCheckbox.checked)) { + remoting.connector.pairingRequested = true; + } } else { remoting.setMode(remoting.AppMode.HOME); } }; pinForm.addEventListener('submit', onSubmitOrCancel, false); pinCancel.addEventListener('click', onSubmitOrCancel, false); - - var rememberPin = document.getElementById('remember-pin'); rememberPin.hidden = !supportsPairing; - var checkbox = /** @type {HTMLInputElement} */ - document.getElementById('remember-pin-checkbox'); - checkbox.checked = false; + rememberPinCheckbox.checked = false; var message = document.getElementById('pin-message'); l10n.localizeElement(message, host.hostName); remoting.setMode(remoting.AppMode.CLIENT_PIN_PROMPT); }; - remoting.connector.connectMe2Me(host, requestPin, fetchThirdPartyToken); + + /** @param {Object} settings */ + var connectMe2MeHostSettingsRetrieved = function(settings) { + /** @type {string} */ + var clientId = ''; + /** @type {string} */ + var sharedSecret = ''; + var pairingInfo = /** @type {Object} */ (settings['pairingInfo']); + if (pairingInfo) { + clientId = /** @type {string} */ (pairingInfo['clientId']); + sharedSecret = /** @type {string} */ (pairingInfo['sharedSecret']); + } + remoting.connector.connectMe2Me(host, requestPin, fetchThirdPartyToken, + clientId, sharedSecret); + } + + remoting.HostSettings.load(host.hostId, connectMe2MeHostSettingsRetrieved); }; /** @param {remoting.ClientSession} clientSession */ @@ -323,4 +342,22 @@ remoting.onConnected = function(clientSession) { remoting.toolbar.preview(); remoting.clipboard.startSession(); updateStatistics_(); + if (remoting.connector.pairingRequested) { + /** + * @param {string} clientId + * @param {string} sharedSecret + */ + var onPairingComplete = function(clientId, sharedSecret) { + var pairingInfo = { + pairingInfo: { + clientId: clientId, + sharedSecret: sharedSecret + } + }; + remoting.HostSettings.save(clientSession.hostId, pairingInfo); + }; + // TODO(jamiewalch): Since we can't get a descriptive name for the local + // computer from Javascript, pass the empty string for now. + clientSession.requestPairing('', onPairingComplete); + } }; diff --git a/remoting/webapp/client_session.js b/remoting/webapp/client_session.js index f40fa85..b81260dc 100644 --- a/remoting/webapp/client_session.js +++ b/remoting/webapp/client_session.js @@ -41,12 +41,17 @@ var remoting = remoting || {}; * Mixed into authentication hashes for some authentication methods. * @param {remoting.ClientSession.Mode} mode The mode of this connection. * @param {string} hostDisplayName The name of the host for display purposes. + * @param {string} clientPairingId For paired Me2Me connections, the + * pairing id for this client, as issued by the host. + * @param {string} clientPairedSecret For paired Me2Me connections, the + * paired secret for this client, as issued by the host. * @constructor */ remoting.ClientSession = function(hostJid, clientJid, hostPublicKey, accessCode, fetchPin, fetchThirdPartyToken, authenticationMethods, hostId, - mode, hostDisplayName) { + mode, hostDisplayName, + clientPairingId, clientPairedSecret) { this.state = remoting.ClientSession.State.CREATED; this.hostJid = hostJid; @@ -59,11 +64,16 @@ remoting.ClientSession = function(hostJid, clientJid, hostPublicKey, accessCode, /** @private */ this.fetchThirdPartyToken_ = fetchThirdPartyToken; this.authenticationMethods = authenticationMethods; + /** @type {string} */ this.hostId = hostId; /** @type {string} */ this.hostDisplayName = hostDisplayName; /** @type {remoting.ClientSession.Mode} */ this.mode = mode; + /** @private */ + this.clientPairingId_ = clientPairingId; + /** @private */ + this.clientPairedSecret_ = clientPairedSecret this.sessionId = ''; /** @type {remoting.ClientPlugin} */ this.plugin = null; @@ -768,8 +778,8 @@ remoting.ClientSession.prototype.connectPluginToWcs_ = function() { */ remoting.ClientSession.prototype.connectToHost_ = function(sharedSecret) { this.plugin.connect(this.hostJid, this.hostPublicKey, this.clientJid, - sharedSecret, this.authenticationMethods, - this.hostId); + sharedSecret, this.authenticationMethods, this.hostId, + this.clientPairingId_, this.clientPairedSecret_); }; /** @@ -1058,6 +1068,19 @@ remoting.ClientSession.prototype.logHostOfflineErrors = function(enable) { }; /** + * Request pairing with the host for PIN-less authentication. + * + * @param {string} clientName The human-readable name of the client. + * @param {function(string, string):void} onDone Callback to receive the + * client id and shared secret when they are available. + */ +remoting.ClientSession.prototype.requestPairing = function(clientName, onDone) { + if (this.plugin) { + this.plugin.requestPairing(clientName, onDone); + } +}; + +/** * Toggles between full-screen and windowed mode. * @return {void} Nothing. * @private diff --git a/remoting/webapp/session_connector.js b/remoting/webapp/session_connector.js index 779a00f..86eff44 100644 --- a/remoting/webapp/session_connector.js +++ b/remoting/webapp/session_connector.js @@ -70,6 +70,14 @@ remoting.SessionConnector = function(pluginParent, onOk, onError) { */ remoting.SessionConnector.prototype.reset = function() { /** + * Set to true to indicate that the user requested pairing when entering + * their PIN for a Me2Me connection. + * + * @type {boolean} + */ + this.pairingRequested = false; + + /** * String used to identify the host to which to connect. For IT2Me, this is * the first 7 digits of the access code; for Me2Me it is the host identifier. * @@ -79,6 +87,23 @@ remoting.SessionConnector.prototype.reset = function() { this.hostId_ = ''; /** + * For paired connections, the client id of this device, issued by the host. + * + * @type {string} + * @private + */ + this.clientPairingId_ = ''; + + /** + * For paired connections, the paired secret for this device, issued by the + * host. + * + * @type {string} + * @private + */ + this.clientPairedSecret_ = ''; + + /** * String used to authenticate to the host on connection. For IT2Me, this is * the access code; for Me2Me it is the PIN. * @@ -153,14 +178,21 @@ remoting.SessionConnector.prototype.reset = function() { * function(string, string): void): void} * fetchThirdPartyToken Function to obtain a token from a third party * authenticaiton server. + * @param {string} clientPairingId The client id issued by the host when + * this device was paired, if it is already paired. + * @param {string} clientPairedSecret The shared secret issued by the host when + * this device was paired, if it is already paired. * @return {void} Nothing. */ -remoting.SessionConnector.prototype.connectMe2Me = function( - host, fetchPin, fetchThirdPartyToken) { +remoting.SessionConnector.prototype.connectMe2Me = + function(host, fetchPin, fetchThirdPartyToken, + clientPairingId, clientPairedSecret) { // Cancel any existing connect operation. this.cancel(); this.hostId_ = host.hostId; + this.clientPairingId_ = clientPairingId; + this.clientPairedSecret_ = clientPairedSecret; this.hostJid_ = host.jabberId; this.hostPublicKey_ = host.publicKey; this.fetchPin_ = fetchPin; @@ -318,7 +350,8 @@ remoting.SessionConnector.prototype.createSessionIfReady_ = function() { this.clientSession_ = new remoting.ClientSession( this.hostJid_, this.clientJid_, this.hostPublicKey_, this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_, securityTypes, this.hostId_, - this.connectionMode_, this.hostDisplayName_); + this.connectionMode_, this.hostDisplayName_, this.clientPairingId_, + this.clientPairedSecret_); this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_); this.clientSession_.setOnStateChange(this.onStateChange_.bind(this)); this.clientSession_.createPluginAndConnect(this.pluginParent_); @@ -408,7 +441,8 @@ remoting.SessionConnector.prototype.onHostListRefresh_ = function(success) { if (success) { var host = remoting.hostList.getHostForId(this.hostId_); if (host) { - this.connectMe2Me(host, this.fetchPin_, this.fetchThirdPartyToken_); + this.connectMe2Me(host, this.fetchPin_, this.fetchThirdPartyToken_, + this.clientPairingId_, this.clientPairedSecret_); return; } } @@ -471,4 +505,4 @@ remoting.SessionConnector.prototype.normalizeAccessCode_ = function(accessCode) { // Trim whitespace. return accessCode.replace(/\s/g, ''); -};
\ No newline at end of file +}; |