diff options
author | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-24 22:05:58 +0000 |
---|---|---|
committer | weitaosu@chromium.org <weitaosu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-24 22:05:58 +0000 |
commit | d0b78c13716ef91150736b8199a7d8f70ef42e86 (patch) | |
tree | 3a8d8466f1db5c307205982315383e1b6dfc4c90 | |
parent | ce29689518e756fe9999c1199058b1016209ab07 (diff) | |
download | chromium_src-d0b78c13716ef91150736b8199a7d8f70ef42e86.zip chromium_src-d0b78c13716ef91150736b8199a7d8f70ef42e86.tar.gz chromium_src-d0b78c13716ef91150736b8199a7d8f70ef42e86.tar.bz2 |
Create a remoting_it2me_host_static GYP target which will be shared between the NPAPI plugin and the it2me native messaging host.
BUG=309844
Review URL: https://codereview.chromium.org/33183006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230835 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/host/it2me/it2me_impl.cc | 468 | ||||
-rw-r--r-- | remoting/host/it2me/it2me_impl.h | 161 | ||||
-rw-r--r-- | remoting/host/plugin/host_script_object.cc | 456 | ||||
-rw-r--r-- | remoting/host/plugin/host_script_object.h | 156 | ||||
-rw-r--r-- | remoting/remoting.gyp | 24 |
5 files changed, 655 insertions, 610 deletions
diff --git a/remoting/host/it2me/it2me_impl.cc b/remoting/host/it2me/it2me_impl.cc new file mode 100644 index 0000000..7b1b4c4 --- /dev/null +++ b/remoting/host/it2me/it2me_impl.cc @@ -0,0 +1,468 @@ +// Copyright 2013 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/host/it2me/it2me_impl.h" + +#include "base/bind.h" +#include "base/strings/string_util.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "net/socket/client_socket_factory.h" +#include "remoting/base/auto_thread.h" +#include "remoting/base/rsa_key_pair.h" +#include "remoting/host/chromoting_host.h" +#include "remoting/host/chromoting_host_context.h" +#include "remoting/host/host_event_logger.h" +#include "remoting/host/host_secret.h" +#include "remoting/host/it2me_desktop_environment.h" +#include "remoting/host/policy_hack/policy_watcher.h" +#include "remoting/host/register_support_host_request.h" +#include "remoting/host/session_manager_factory.h" +#include "remoting/jingle_glue/network_settings.h" +#include "remoting/protocol/it2me_host_authenticator_factory.h" + +namespace remoting { + +namespace { + +// This is used for tagging system event logs. +const char kApplicationName[] = "chromoting"; +const int kMaxLoginAttempts = 5; + +} // namespace + +It2MeImpl::It2MeImpl( + scoped_ptr<ChromotingHostContext> host_context, + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, + base::WeakPtr<It2MeImpl::Observer> observer, + const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, + const std::string& directory_bot_jid) + : host_context_(host_context.Pass()), + plugin_task_runner_(plugin_task_runner), + observer_(observer), + xmpp_server_config_(xmpp_server_config), + directory_bot_jid_(directory_bot_jid), + state_(kDisconnected), + failed_login_attempts_(0), + nat_traversal_enabled_(false), + policy_received_(false) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); +} + +void It2MeImpl::Connect() { + if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->ui_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Connect, this)); + return; + } + + desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory( + host_context_->network_task_runner(), + host_context_->input_task_runner(), + host_context_->ui_task_runner())); + + // Start monitoring configured policies. + policy_watcher_.reset( + policy_hack::PolicyWatcher::Create(host_context_->network_task_runner())); + policy_watcher_->StartWatching( + base::Bind(&It2MeImpl::OnPolicyUpdate, this)); + + // Switch to the network thread to start the actual connection. + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::ReadPolicyAndConnect, this)); +} + +void It2MeImpl::Disconnect() { + if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Disconnect, this)); + return; + } + + switch (state_) { + case kDisconnected: + ShutdownOnNetworkThread(); + return; + + case kStarting: + SetState(kDisconnecting); + SetState(kDisconnected); + ShutdownOnNetworkThread(); + return; + + case kDisconnecting: + return; + + default: + SetState(kDisconnecting); + + if (!host_) { + SetState(kDisconnected); + ShutdownOnNetworkThread(); + return; + } + + // Deleting the host destroys SignalStrategy synchronously, but + // SignalStrategy::Listener handlers are not allowed to destroy + // SignalStrategy, so post task to destroy the host later. + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnNetworkThread, this)); + return; + } +} + +void It2MeImpl::RequestNatPolicy() { + if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { + DCHECK(plugin_task_runner_->BelongsToCurrentThread()); + host_context_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::RequestNatPolicy, this)); + return; + } + + if (policy_received_) + UpdateNatPolicy(nat_traversal_enabled_); +} + +void It2MeImpl::ReadPolicyAndConnect() { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + SetState(kStarting); + + // Only proceed to FinishConnect() if at least one policy update has been + // received. + if (policy_received_) { + FinishConnect(); + } else { + // Otherwise, create the policy watcher, and thunk the connect. + pending_connect_ = + base::Bind(&It2MeImpl::FinishConnect, this); + } +} + +void It2MeImpl::FinishConnect() { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (state_ != kStarting) { + // Host has been stopped while we were fetching policy. + return; + } + + // Check the host domain policy. + if (!required_host_domain_.empty() && + !EndsWith(xmpp_server_config_.username, + std::string("@") + required_host_domain_, false)) { + SetState(kInvalidDomainError); + return; + } + + // Generate a key pair for the Host to use. + // TODO(wez): Move this to the worker thread. + host_key_pair_ = RsaKeyPair::Generate(); + + // Create XMPP connection. + scoped_ptr<SignalStrategy> signal_strategy( + new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), + host_context_->url_request_context_getter(), + xmpp_server_config_)); + + // Request registration of the host for support. + scoped_ptr<RegisterSupportHostRequest> register_request( + new RegisterSupportHostRequest( + signal_strategy.get(), host_key_pair_, directory_bot_jid_, + base::Bind(&It2MeImpl::OnReceivedSupportID, + base::Unretained(this)))); + + // Beyond this point nothing can fail, so save the config and request. + signal_strategy_ = signal_strategy.Pass(); + register_request_ = register_request.Pass(); + + // If NAT traversal is off then limit port range to allow firewall pin-holing. + LOG(INFO) << "NAT state: " << nat_traversal_enabled_; + NetworkSettings network_settings( + nat_traversal_enabled_ ? + NetworkSettings::NAT_TRAVERSAL_ENABLED : + NetworkSettings::NAT_TRAVERSAL_DISABLED); + if (!nat_traversal_enabled_) { + network_settings.min_port = NetworkSettings::kDefaultMinPort; + network_settings.max_port = NetworkSettings::kDefaultMaxPort; + } + + // Create the host. + host_.reset(new ChromotingHost( + signal_strategy_.get(), + desktop_environment_factory_.get(), + CreateHostSessionManager(network_settings, + host_context_->url_request_context_getter()), + host_context_->audio_task_runner(), + host_context_->input_task_runner(), + host_context_->video_capture_task_runner(), + host_context_->video_encode_task_runner(), + host_context_->network_task_runner(), + host_context_->ui_task_runner())); + host_->AddStatusObserver(this); + log_to_server_.reset( + new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME, + signal_strategy_.get(), directory_bot_jid_)); + + // Disable audio by default. + // TODO(sergeyu): Add UI to enable it. + scoped_ptr<protocol::CandidateSessionConfig> protocol_config = + protocol::CandidateSessionConfig::CreateDefault(); + protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get()); + + // VP9 encode is not yet supported. + protocol::CandidateSessionConfig::DisableVideoCodec( + protocol_config.get(), protocol::ChannelConfig::CODEC_VP9); + + host_->set_protocol_config(protocol_config.Pass()); + + // Create event logger. + host_event_logger_ = + HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName); + + // Connect signaling and start the host. + signal_strategy_->Connect(); + host_->Start(xmpp_server_config_.username); + + SetState(kRequestedAccessCode); + return; +} + +void It2MeImpl::ShutdownOnNetworkThread() { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + DCHECK(state_ == kDisconnecting || state_ == kDisconnected); + + if (state_ == kDisconnecting) { + host_event_logger_.reset(); + host_->RemoveStatusObserver(this); + host_.reset(); + + register_request_.reset(); + log_to_server_.reset(); + signal_strategy_.reset(); + SetState(kDisconnected); + } + + host_context_->ui_task_runner()->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnUiThread, this)); +} + +void It2MeImpl::ShutdownOnUiThread() { + DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread()); + + // Destroy the DesktopEnvironmentFactory, to free thread references. + desktop_environment_factory_.reset(); + + // Stop listening for policy updates. + if (policy_watcher_.get()) { + base::WaitableEvent policy_watcher_stopped_(true, false); + policy_watcher_->StopWatching(&policy_watcher_stopped_); + policy_watcher_stopped_.Wait(); + policy_watcher_.reset(); + } +} + +void It2MeImpl::OnAccessDenied(const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + ++failed_login_attempts_; + if (failed_login_attempts_ == kMaxLoginAttempts) { + Disconnect(); + } +} + +void It2MeImpl::OnClientAuthenticated( + const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (state_ == kDisconnecting) { + // Ignore the new connection if we are disconnecting. + return; + } + if (state_ == kConnected) { + // If we already connected another client then one of the connections may be + // an attacker, so both are suspect and we have to reject the second + // connection and shutdown the host. + host_->RejectAuthenticatingClient(); + Disconnect(); + return; + } + + std::string client_username = jid; + size_t pos = client_username.find('/'); + if (pos != std::string::npos) + client_username.replace(pos, std::string::npos, ""); + + LOG(INFO) << "Client " << client_username << " connected."; + + // Pass the client user name to the script object before changing state. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Observer::OnClientAuthenticated, + observer_, client_username)); + + SetState(kConnected); +} + +void It2MeImpl::OnClientDisconnected( + const std::string& jid) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + Disconnect(); +} + +void It2MeImpl::OnPolicyUpdate( + scoped_ptr<base::DictionaryValue> policies) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + bool nat_policy; + if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, + &nat_policy)) { + UpdateNatPolicy(nat_policy); + } + std::string host_domain; + if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, + &host_domain)) { + UpdateHostDomainPolicy(host_domain); + } + + policy_received_ = true; + + if (!pending_connect_.is_null()) { + pending_connect_.Run(); + pending_connect_.Reset(); + } +} + +void It2MeImpl::UpdateNatPolicy( + bool nat_traversal_enabled) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled; + + // When transitioning from enabled to disabled, force disconnect any + // existing session. + if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) { + Disconnect(); + } + + nat_traversal_enabled_ = nat_traversal_enabled; + + // Notify the web-app of the policy setting. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Observer::OnNatPolicyChanged, + observer_, nat_traversal_enabled_)); +} + +void It2MeImpl::UpdateHostDomainPolicy( + const std::string& host_domain) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + VLOG(2) << "UpdateHostDomainPolicy: " << host_domain; + + // When setting a host domain policy, force disconnect any existing session. + if (!host_domain.empty() && IsConnected()) { + Disconnect(); + } + + required_host_domain_ = host_domain; +} + +It2MeImpl::~It2MeImpl() { + // Check that resources that need to be torn down on the UI thread are gone. + DCHECK(!desktop_environment_factory_.get()); + DCHECK(!policy_watcher_.get()); +} + +void It2MeImpl::SetState(It2MeHostState state) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + switch (state_) { + case kDisconnected: + DCHECK(state == kStarting || + state == kError) << state; + break; + case kStarting: + DCHECK(state == kRequestedAccessCode || + state == kDisconnecting || + state == kError || + state == kInvalidDomainError) << state; + break; + case kRequestedAccessCode: + DCHECK(state == kReceivedAccessCode || + state == kDisconnecting || + state == kError) << state; + break; + case kReceivedAccessCode: + DCHECK(state == kConnected || + state == kDisconnecting || + state == kError) << state; + break; + case kConnected: + DCHECK(state == kDisconnecting || + state == kDisconnected || + state == kError) << state; + break; + case kDisconnecting: + DCHECK(state == kDisconnected) << state; + break; + case kError: + DCHECK(state == kDisconnecting) << state; + break; + case kInvalidDomainError: + DCHECK(state == kDisconnecting) << state; + break; + }; + + state_ = state; + + // Post a state-change notification to the web-app. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Observer::OnStateChanged, + observer_, state)); +} + +bool It2MeImpl::IsConnected() const { + return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode || + state_ == kConnected; +} + +void It2MeImpl::OnReceivedSupportID( + bool success, + const std::string& support_id, + const base::TimeDelta& lifetime) { + DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); + + if (!success) { + SetState(kError); + Disconnect(); + return; + } + + std::string host_secret = GenerateSupportHostSecret(); + std::string access_code = support_id + host_secret; + + std::string local_certificate = host_key_pair_->GenerateCertificate(); + if (local_certificate.empty()) { + LOG(ERROR) << "Failed to generate host certificate."; + SetState(kError); + Disconnect(); + return; + } + + scoped_ptr<protocol::AuthenticatorFactory> factory( + new protocol::It2MeHostAuthenticatorFactory( + local_certificate, host_key_pair_, access_code)); + host_->SetAuthenticatorFactory(factory.Pass()); + + // Pass the Access Code to the script object before changing state. + plugin_task_runner_->PostTask( + FROM_HERE, base::Bind(&It2MeImpl::Observer::OnStoreAccessCode, + observer_, access_code, lifetime)); + + SetState(kReceivedAccessCode); +} + +} // namespace remoting diff --git a/remoting/host/it2me/it2me_impl.h b/remoting/host/it2me/it2me_impl.h new file mode 100644 index 0000000..f31709b --- /dev/null +++ b/remoting/host/it2me/it2me_impl.h @@ -0,0 +1,161 @@ +// Copyright 2013 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_HOST_IT2ME__IT2ME_IMPL_H_ +#define REMOTING_HOST_IT2ME__IT2ME_IMPL_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "remoting/host/log_to_server.h" +#include "remoting/jingle_glue/xmpp_signal_strategy.h" + +namespace remoting { + +class RegisterSupportHostRequest; +class HostNPScriptObject; +class DesktopEnvironmentFactory; +class HostEventLogger; +class ChromotingHost; +class ChromotingHostContext; + +namespace policy_hack { + +class PolicyWatcher; + +} // namespace policy_hack + +// These state values are duplicated in host_session.js. Remember to update +// both copies when making changes. +enum It2MeHostState { + kDisconnected, + kStarting, + kRequestedAccessCode, + kReceivedAccessCode, + kConnected, + kDisconnecting, + kError, + kInvalidDomainError +}; + +// Internal implementation of the plugin's It2Me host function. +class It2MeImpl + : public base::RefCountedThreadSafe<It2MeImpl>, + public HostStatusObserver { + public: + + class Observer { + public: + virtual void OnClientAuthenticated(const std::string& client_username) = 0; + virtual void OnStoreAccessCode(const std::string& access_code, + base::TimeDelta access_code_lifetime) = 0; + virtual void OnNatPolicyChanged(bool nat_traversal_enabled) = 0; + virtual void OnStateChanged(It2MeHostState state) = 0; + }; + + It2MeImpl( + scoped_ptr<ChromotingHostContext> context, + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, + base::WeakPtr<It2MeImpl::Observer> observer, + const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, + const std::string& directory_bot_jid); + + // Methods called by the script object, from the plugin thread. + + // Creates It2Me host structures and starts the host. + void Connect(); + + // Disconnects the host, ready for tear-down. + // Also called internally, from the network thread. + void Disconnect(); + + // Request a NAT policy notification. + void RequestNatPolicy(); + + // remoting::HostStatusObserver implementation. + virtual void OnAccessDenied(const std::string& jid) OVERRIDE; + virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; + virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; + + private: + friend class base::RefCountedThreadSafe<It2MeImpl>; + + virtual ~It2MeImpl(); + + // Updates state of the host. Can be called only on the network thread. + void SetState(It2MeHostState state); + + // Returns true if the host is connected. + bool IsConnected() const; + + // Called by Connect() to check for policies and start connection process. + void ReadPolicyAndConnect(); + + // Called by ReadPolicyAndConnect once policies have been read. + void FinishConnect(); + + // Called when the support host registration completes. + void OnReceivedSupportID(bool success, + const std::string& support_id, + const base::TimeDelta& lifetime); + + // Shuts down |host_| on the network thread and posts ShutdownOnUiThread() + // to shut down UI thread resources. + void ShutdownOnNetworkThread(); + + // Shuts down |desktop_environment_factory_| and |policy_watcher_| on + // the UI thread. + void ShutdownOnUiThread(); + + // Called when initial policies are read, and when they change. + void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); + + // Handlers for NAT traversal and host domain policies. + void UpdateNatPolicy(bool nat_traversal_enabled); + void UpdateHostDomainPolicy(const std::string& host_domain); + + // Caller supplied fields. + scoped_ptr<ChromotingHostContext> host_context_; + scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner_; + base::WeakPtr<It2MeImpl::Observer> observer_; + XmppSignalStrategy::XmppServerConfig xmpp_server_config_; + std::string directory_bot_jid_; + + It2MeHostState state_; + + scoped_refptr<RsaKeyPair> host_key_pair_; + scoped_ptr<SignalStrategy> signal_strategy_; + scoped_ptr<RegisterSupportHostRequest> register_request_; + scoped_ptr<LogToServer> log_to_server_; + scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; + scoped_ptr<HostEventLogger> host_event_logger_; + + scoped_ptr<ChromotingHost> host_; + int failed_login_attempts_; + + scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; + + // Host the current nat traversal policy setting. + bool nat_traversal_enabled_; + + // The host domain policy setting. + std::string required_host_domain_; + + // Indicates whether or not a policy has ever been read. This is to ensure + // that on startup, we do not accidentally start a connection before we have + // queried our policy restrictions. + bool policy_received_; + + // On startup, it is possible to have Connect() called before the policy read + // is completed. Rather than just failing, we thunk the connection call so + // it can be executed after at least one successful policy read. This + // variable contains the thunk if it is necessary. + base::Closure pending_connect_; + + DISALLOW_COPY_AND_ASSIGN(It2MeImpl); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_IT2ME__IT2ME_IMPL_H_ diff --git a/remoting/host/plugin/host_script_object.cc b/remoting/host/plugin/host_script_object.cc index 1b173db..66d228c 100644 --- a/remoting/host/plugin/host_script_object.cc +++ b/remoting/host/plugin/host_script_object.cc @@ -7,46 +7,29 @@ #include "base/bind.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/message_loop/message_loop.h" -#include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/platform_thread.h" -#include "base/values.h" -#include "net/base/net_util.h" -#include "net/socket/client_socket_factory.h" #include "remoting/base/auth_token_util.h" #include "remoting/base/auto_thread.h" #include "remoting/base/resources.h" #include "remoting/base/rsa_key_pair.h" -#include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/host_config.h" -#include "remoting/host/host_event_logger.h" -#include "remoting/host/host_secret.h" -#include "remoting/host/host_status_observer.h" -#include "remoting/host/it2me_desktop_environment.h" #include "remoting/host/pairing_registry_delegate.h" #include "remoting/host/pin_hash.h" #include "remoting/host/plugin/host_log_handler.h" #include "remoting/host/policy_hack/policy_watcher.h" -#include "remoting/host/register_support_host_request.h" #include "remoting/host/service_urls.h" -#include "remoting/host/session_manager_factory.h" -#include "remoting/jingle_glue/network_settings.h" -#include "remoting/jingle_glue/xmpp_signal_strategy.h" -#include "remoting/protocol/it2me_host_authenticator_factory.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npfunctions.h" #include "third_party/npapi/bindings/npruntime.h" namespace remoting { namespace { -// This is used for tagging system event logs. -const char kApplicationName[] = "chromoting"; - const char* kAttrNameAccessCode = "accessCode"; const char* kAttrNameAccessCodeLifetime = "accessCodeLifetime"; const char* kAttrNameClient = "client"; @@ -86,446 +69,11 @@ const char* kAttrNameDisconnecting = "DISCONNECTING"; const char* kAttrNameError = "ERROR"; const char* kAttrNameInvalidDomainError = "INVALID_DOMAIN_ERROR"; -const int kMaxLoginAttempts = 5; - // Space separated list of features supported in addition to the base protocol. const char* kSupportedFeatures = "pairingRegistry"; } // namespace -It2MeImpl::It2MeImpl( - scoped_ptr<ChromotingHostContext> host_context, - scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, - base::WeakPtr<It2MeImpl::Observer> observer, - const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, - const std::string& directory_bot_jid) - : host_context_(host_context.Pass()), - plugin_task_runner_(plugin_task_runner), - observer_(observer), - xmpp_server_config_(xmpp_server_config), - directory_bot_jid_(directory_bot_jid), - state_(kDisconnected), - failed_login_attempts_(0), - nat_traversal_enabled_(false), - policy_received_(false) { - DCHECK(plugin_task_runner_->BelongsToCurrentThread()); -} - -void It2MeImpl::Connect() { - if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) { - DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - host_context_->ui_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Connect, this)); - return; - } - - desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory( - host_context_->network_task_runner(), - host_context_->input_task_runner(), - host_context_->ui_task_runner())); - - // Start monitoring configured policies. - policy_watcher_.reset( - policy_hack::PolicyWatcher::Create(host_context_->network_task_runner())); - policy_watcher_->StartWatching( - base::Bind(&It2MeImpl::OnPolicyUpdate, this)); - - // Switch to the network thread to start the actual connection. - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::ReadPolicyAndConnect, this)); -} - -void It2MeImpl::Disconnect() { - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Disconnect, this)); - return; - } - - switch (state_) { - case kDisconnected: - ShutdownOnNetworkThread(); - return; - - case kStarting: - SetState(kDisconnecting); - SetState(kDisconnected); - ShutdownOnNetworkThread(); - return; - - case kDisconnecting: - return; - - default: - SetState(kDisconnecting); - - if (!host_) { - SetState(kDisconnected); - ShutdownOnNetworkThread(); - return; - } - - // Deleting the host destroys SignalStrategy synchronously, but - // SignalStrategy::Listener handlers are not allowed to destroy - // SignalStrategy, so post task to destroy the host later. - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnNetworkThread, this)); - return; - } -} - -void It2MeImpl::RequestNatPolicy() { - if (!host_context_->network_task_runner()->BelongsToCurrentThread()) { - DCHECK(plugin_task_runner_->BelongsToCurrentThread()); - host_context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::RequestNatPolicy, this)); - return; - } - - if (policy_received_) - UpdateNatPolicy(nat_traversal_enabled_); -} - -void It2MeImpl::ReadPolicyAndConnect() { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - SetState(kStarting); - - // Only proceed to FinishConnect() if at least one policy update has been - // received. - if (policy_received_) { - FinishConnect(); - } else { - // Otherwise, create the policy watcher, and thunk the connect. - pending_connect_ = - base::Bind(&It2MeImpl::FinishConnect, this); - } -} - -void It2MeImpl::FinishConnect() { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - if (state_ != kStarting) { - // Host has been stopped while we were fetching policy. - return; - } - - // Check the host domain policy. - if (!required_host_domain_.empty() && - !EndsWith(xmpp_server_config_.username, - std::string("@") + required_host_domain_, false)) { - SetState(kInvalidDomainError); - return; - } - - // Generate a key pair for the Host to use. - // TODO(wez): Move this to the worker thread. - host_key_pair_ = RsaKeyPair::Generate(); - - // Create XMPP connection. - scoped_ptr<SignalStrategy> signal_strategy( - new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), - host_context_->url_request_context_getter(), - xmpp_server_config_)); - - // Request registration of the host for support. - scoped_ptr<RegisterSupportHostRequest> register_request( - new RegisterSupportHostRequest( - signal_strategy.get(), host_key_pair_, directory_bot_jid_, - base::Bind(&It2MeImpl::OnReceivedSupportID, - base::Unretained(this)))); - - // Beyond this point nothing can fail, so save the config and request. - signal_strategy_ = signal_strategy.Pass(); - register_request_ = register_request.Pass(); - - // If NAT traversal is off then limit port range to allow firewall pin-holing. - LOG(INFO) << "NAT state: " << nat_traversal_enabled_; - NetworkSettings network_settings( - nat_traversal_enabled_ ? - NetworkSettings::NAT_TRAVERSAL_ENABLED : - NetworkSettings::NAT_TRAVERSAL_DISABLED); - if (!nat_traversal_enabled_) { - network_settings.min_port = NetworkSettings::kDefaultMinPort; - network_settings.max_port = NetworkSettings::kDefaultMaxPort; - } - - // Create the host. - host_.reset(new ChromotingHost( - signal_strategy_.get(), - desktop_environment_factory_.get(), - CreateHostSessionManager(network_settings, - host_context_->url_request_context_getter()), - host_context_->audio_task_runner(), - host_context_->input_task_runner(), - host_context_->video_capture_task_runner(), - host_context_->video_encode_task_runner(), - host_context_->network_task_runner(), - host_context_->ui_task_runner())); - host_->AddStatusObserver(this); - log_to_server_.reset( - new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME, - signal_strategy_.get(), directory_bot_jid_)); - - // Disable audio by default. - // TODO(sergeyu): Add UI to enable it. - scoped_ptr<protocol::CandidateSessionConfig> protocol_config = - protocol::CandidateSessionConfig::CreateDefault(); - protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get()); - - // VP9 encode is not yet supported. - protocol::CandidateSessionConfig::DisableVideoCodec( - protocol_config.get(), protocol::ChannelConfig::CODEC_VP9); - - host_->set_protocol_config(protocol_config.Pass()); - - // Create event logger. - host_event_logger_ = - HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName); - - // Connect signaling and start the host. - signal_strategy_->Connect(); - host_->Start(xmpp_server_config_.username); - - SetState(kRequestedAccessCode); - return; -} - -void It2MeImpl::ShutdownOnNetworkThread() { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - DCHECK(state_ == kDisconnecting || state_ == kDisconnected); - - if (state_ == kDisconnecting) { - host_event_logger_.reset(); - host_->RemoveStatusObserver(this); - host_.reset(); - - register_request_.reset(); - log_to_server_.reset(); - signal_strategy_.reset(); - SetState(kDisconnected); - } - - host_context_->ui_task_runner()->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::ShutdownOnUiThread, this)); -} - -void It2MeImpl::ShutdownOnUiThread() { - DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread()); - - // Destroy the DesktopEnvironmentFactory, to free thread references. - desktop_environment_factory_.reset(); - - // Stop listening for policy updates. - if (policy_watcher_.get()) { - base::WaitableEvent policy_watcher_stopped_(true, false); - policy_watcher_->StopWatching(&policy_watcher_stopped_); - policy_watcher_stopped_.Wait(); - policy_watcher_.reset(); - } -} - -void It2MeImpl::OnAccessDenied(const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - ++failed_login_attempts_; - if (failed_login_attempts_ == kMaxLoginAttempts) { - Disconnect(); - } -} - -void It2MeImpl::OnClientAuthenticated( - const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - if (state_ == kDisconnecting) { - // Ignore the new connection if we are disconnecting. - return; - } - if (state_ == kConnected) { - // If we already connected another client then one of the connections may be - // an attacker, so both are suspect and we have to reject the second - // connection and shutdown the host. - host_->RejectAuthenticatingClient(); - Disconnect(); - return; - } - - std::string client_username = jid; - size_t pos = client_username.find('/'); - if (pos != std::string::npos) - client_username.replace(pos, std::string::npos, ""); - - LOG(INFO) << "Client " << client_username << " connected."; - - // Pass the client user name to the script object before changing state. - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Observer::OnClientAuthenticated, - observer_, client_username)); - - SetState(kConnected); -} - -void It2MeImpl::OnClientDisconnected( - const std::string& jid) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - Disconnect(); -} - -void It2MeImpl::OnPolicyUpdate( - scoped_ptr<base::DictionaryValue> policies) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - bool nat_policy; - if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, - &nat_policy)) { - UpdateNatPolicy(nat_policy); - } - std::string host_domain; - if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, - &host_domain)) { - UpdateHostDomainPolicy(host_domain); - } - - policy_received_ = true; - - if (!pending_connect_.is_null()) { - pending_connect_.Run(); - pending_connect_.Reset(); - } -} - -void It2MeImpl::UpdateNatPolicy( - bool nat_traversal_enabled) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled; - - // When transitioning from enabled to disabled, force disconnect any - // existing session. - if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) { - Disconnect(); - } - - nat_traversal_enabled_ = nat_traversal_enabled; - - // Notify the web-app of the policy setting. - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Observer::OnNatPolicyChanged, - observer_, nat_traversal_enabled_)); -} - -void It2MeImpl::UpdateHostDomainPolicy( - const std::string& host_domain) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - VLOG(2) << "UpdateHostDomainPolicy: " << host_domain; - - // When setting a host domain policy, force disconnect any existing session. - if (!host_domain.empty() && IsConnected()) { - Disconnect(); - } - - required_host_domain_ = host_domain; -} - -It2MeImpl::~It2MeImpl() { - // Check that resources that need to be torn down on the UI thread are gone. - DCHECK(!desktop_environment_factory_.get()); - DCHECK(!policy_watcher_.get()); -} - -void It2MeImpl::SetState(It2MeHostState state) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - switch (state_) { - case kDisconnected: - DCHECK(state == kStarting || - state == kError) << state; - break; - case kStarting: - DCHECK(state == kRequestedAccessCode || - state == kDisconnecting || - state == kError || - state == kInvalidDomainError) << state; - break; - case kRequestedAccessCode: - DCHECK(state == kReceivedAccessCode || - state == kDisconnecting || - state == kError) << state; - break; - case kReceivedAccessCode: - DCHECK(state == kConnected || - state == kDisconnecting || - state == kError) << state; - break; - case kConnected: - DCHECK(state == kDisconnecting || - state == kDisconnected || - state == kError) << state; - break; - case kDisconnecting: - DCHECK(state == kDisconnected) << state; - break; - case kError: - DCHECK(state == kDisconnecting) << state; - break; - case kInvalidDomainError: - DCHECK(state == kDisconnecting) << state; - break; - }; - - state_ = state; - - // Post a state-change notification to the web-app. - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Observer::OnStateChanged, - observer_, state)); -} - -bool It2MeImpl::IsConnected() const { - return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode || - state_ == kConnected; -} - -void It2MeImpl::OnReceivedSupportID( - bool success, - const std::string& support_id, - const base::TimeDelta& lifetime) { - DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); - - if (!success) { - SetState(kError); - Disconnect(); - return; - } - - std::string host_secret = GenerateSupportHostSecret(); - std::string access_code = support_id + host_secret; - - std::string local_certificate = host_key_pair_->GenerateCertificate(); - if (local_certificate.empty()) { - LOG(ERROR) << "Failed to generate host certificate."; - SetState(kError); - Disconnect(); - return; - } - - scoped_ptr<protocol::AuthenticatorFactory> factory( - new protocol::It2MeHostAuthenticatorFactory( - local_certificate, host_key_pair_, access_code)); - host_->SetAuthenticatorFactory(factory.Pass()); - - // Pass the Access Code to the script object before changing state. - plugin_task_runner_->PostTask( - FROM_HERE, base::Bind(&It2MeImpl::Observer::OnStoreAccessCode, - observer_, access_code, lifetime)); - - SetState(kReceivedAccessCode); -} - HostNPScriptObject::HostNPScriptObject( NPP plugin, NPObject* parent, diff --git a/remoting/host/plugin/host_script_object.h b/remoting/host/plugin/host_script_object.h index afcc983..74abcd9 100644 --- a/remoting/host/plugin/host_script_object.h +++ b/remoting/host/plugin/host_script_object.h @@ -8,173 +8,19 @@ #include <string> #include <vector> -#include "base/compiler_specific.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" -#include "base/strings/string16.h" -#include "base/synchronization/cancellation_flag.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" #include "base/thread_task_runner_handle.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" #include "base/time/time.h" #include "remoting/base/auto_thread_task_runner.h" -#include "remoting/host/chromoting_host_context.h" -#include "remoting/host/log_to_server.h" +#include "remoting/host/it2me/it2me_impl.h" #include "remoting/host/plugin/host_plugin_utils.h" #include "remoting/host/setup/daemon_controller.h" #include "remoting/jingle_glue/xmpp_signal_strategy.h" #include "remoting/protocol/pairing_registry.h" -#include "third_party/npapi/bindings/npapi.h" -#include "third_party/npapi/bindings/npfunctions.h" -#include "third_party/npapi/bindings/npruntime.h" namespace remoting { -class RegisterSupportHostRequest; -class HostNPScriptObject; -class DesktopEnvironmentFactory; -class HostEventLogger; -class ChromotingHost; - -namespace policy_hack { - -class PolicyWatcher; - -} // namespace policy_hack - -// These state values are duplicated in host_session.js. Remember to update -// both copies when making changes. -enum It2MeHostState { - kDisconnected, - kStarting, - kRequestedAccessCode, - kReceivedAccessCode, - kConnected, - kDisconnecting, - kError, - kInvalidDomainError -}; - -// Internal implementation of the plugin's It2Me host function. -class It2MeImpl - : public base::RefCountedThreadSafe<It2MeImpl>, - public HostStatusObserver { - public: - - class Observer { - public: - virtual void OnClientAuthenticated(const std::string& client_username) = 0; - virtual void OnStoreAccessCode(const std::string& access_code, - base::TimeDelta access_code_lifetime) = 0; - virtual void OnNatPolicyChanged(bool nat_traversal_enabled) = 0; - virtual void OnStateChanged(It2MeHostState state) = 0; - }; - - It2MeImpl( - scoped_ptr<ChromotingHostContext> context, - scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner, - base::WeakPtr<It2MeImpl::Observer> observer, - const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, - const std::string& directory_bot_jid); - - // Methods called by the script object, from the plugin thread. - - // Creates It2Me host structures and starts the host. - void Connect(); - - // Disconnects the host, ready for tear-down. - // Also called internally, from the network thread. - void Disconnect(); - - // Request a NAT policy notification. - void RequestNatPolicy(); - - // remoting::HostStatusObserver implementation. - virtual void OnAccessDenied(const std::string& jid) OVERRIDE; - virtual void OnClientAuthenticated(const std::string& jid) OVERRIDE; - virtual void OnClientDisconnected(const std::string& jid) OVERRIDE; - - private: - friend class base::RefCountedThreadSafe<It2MeImpl>; - - virtual ~It2MeImpl(); - - // Updates state of the host. Can be called only on the network thread. - void SetState(It2MeHostState state); - - // Returns true if the host is connected. - bool IsConnected() const; - - // Called by Connect() to check for policies and start connection process. - void ReadPolicyAndConnect(); - - // Called by ReadPolicyAndConnect once policies have been read. - void FinishConnect(); - - // Called when the support host registration completes. - void OnReceivedSupportID(bool success, - const std::string& support_id, - const base::TimeDelta& lifetime); - - // Shuts down |host_| on the network thread and posts ShutdownOnUiThread() - // to shut down UI thread resources. - void ShutdownOnNetworkThread(); - - // Shuts down |desktop_environment_factory_| and |policy_watcher_| on - // the UI thread. - void ShutdownOnUiThread(); - - // Called when initial policies are read, and when they change. - void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); - - // Handlers for NAT traversal and host domain policies. - void UpdateNatPolicy(bool nat_traversal_enabled); - void UpdateHostDomainPolicy(const std::string& host_domain); - - // Caller supplied fields. - scoped_ptr<ChromotingHostContext> host_context_; - scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner_; - base::WeakPtr<It2MeImpl::Observer> observer_; - XmppSignalStrategy::XmppServerConfig xmpp_server_config_; - std::string directory_bot_jid_; - - It2MeHostState state_; - - scoped_refptr<RsaKeyPair> host_key_pair_; - scoped_ptr<SignalStrategy> signal_strategy_; - scoped_ptr<RegisterSupportHostRequest> register_request_; - scoped_ptr<LogToServer> log_to_server_; - scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; - scoped_ptr<HostEventLogger> host_event_logger_; - - scoped_ptr<ChromotingHost> host_; - int failed_login_attempts_; - - scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; - - // Host the current nat traversal policy setting. - bool nat_traversal_enabled_; - - // The host domain policy setting. - std::string required_host_domain_; - - // Indicates whether or not a policy has ever been read. This is to ensure - // that on startup, we do not accidentally start a connection before we have - // queried our policy restrictions. - bool policy_received_; - - // On startup, it is possible to have Connect() called before the policy read - // is completed. Rather than just failing, we thunk the connection call so - // it can be executed after at least one successful policy read. This - // variable contains the thunk if it is necessary. - base::Closure pending_connect_; - - DISALLOW_COPY_AND_ASSIGN(It2MeImpl); -}; - // NPAPI plugin implementation for remoting host script object. // HostNPScriptObject creates threads that are required to run // ChromotingHost and starts/stops the host on those threads. When diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 97f7a742..0567506 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -640,8 +640,9 @@ 'remoting_host', 'remoting_host_event_logger', 'remoting_host_logging', - 'remoting_infoplist_strings', 'remoting_host_setup_base', + 'remoting_infoplist_strings', + 'remoting_it2me_host_static', 'remoting_jingle_glue', 'remoting_resources', ], @@ -732,6 +733,27 @@ ], }, # end of target 'remoting_host_plugin' { + 'target_name': 'remoting_it2me_host_static', + 'type': 'static_library', + 'variables': { 'enable_wexit_time_destructors': 1, }, + 'dependencies': [ + '../base/base.gyp:base_i18n', + '../net/net.gyp:net', + 'remoting_base', + 'remoting_host', + 'remoting_host_event_logger', + 'remoting_host_logging', + 'remoting_infoplist_strings', + 'remoting_host_setup_base', + 'remoting_jingle_glue', + 'remoting_resources', + ], + 'sources': [ + 'host/it2me/it2me_impl.cc', + 'host/it2me/it2me_impl.h', + ], + }, # end of target 'remoting_it2me_host_static' + { 'target_name': 'remoting_infoplist_strings', 'type': 'none', 'dependencies': [ |