Home | History | Annotate | Download | only in it2me
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/it2me/it2me_host.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/synchronization/waitable_event.h"
     10 #include "base/threading/platform_thread.h"
     11 #include "net/socket/client_socket_factory.h"
     12 #include "remoting/base/auto_thread.h"
     13 #include "remoting/base/logging.h"
     14 #include "remoting/base/rsa_key_pair.h"
     15 #include "remoting/host/chromoting_host.h"
     16 #include "remoting/host/chromoting_host_context.h"
     17 #include "remoting/host/host_event_logger.h"
     18 #include "remoting/host/host_secret.h"
     19 #include "remoting/host/it2me_desktop_environment.h"
     20 #include "remoting/host/policy_hack/policy_watcher.h"
     21 #include "remoting/host/register_support_host_request.h"
     22 #include "remoting/host/session_manager_factory.h"
     23 #include "remoting/jingle_glue/network_settings.h"
     24 #include "remoting/protocol/it2me_host_authenticator_factory.h"
     25 
     26 namespace remoting {
     27 
     28 namespace {
     29 
     30 // This is used for tagging system event logs.
     31 const char kApplicationName[] = "chromoting";
     32 const int kMaxLoginAttempts = 5;
     33 
     34 }  // namespace
     35 
     36 It2MeHost::It2MeHost(
     37     ChromotingHostContext* host_context,
     38     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     39     base::WeakPtr<It2MeHost::Observer> observer,
     40     const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
     41     const std::string& directory_bot_jid)
     42   : host_context_(host_context),
     43     task_runner_(task_runner),
     44     observer_(observer),
     45     xmpp_server_config_(xmpp_server_config),
     46     directory_bot_jid_(directory_bot_jid),
     47     state_(kDisconnected),
     48     failed_login_attempts_(0),
     49     nat_traversal_enabled_(false),
     50     policy_received_(false) {
     51   DCHECK(task_runner_->BelongsToCurrentThread());
     52 }
     53 
     54 void It2MeHost::Connect() {
     55   if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
     56     DCHECK(task_runner_->BelongsToCurrentThread());
     57     host_context_->ui_task_runner()->PostTask(
     58         FROM_HERE, base::Bind(&It2MeHost::Connect, this));
     59     return;
     60   }
     61 
     62   desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
     63       host_context_->network_task_runner(),
     64       host_context_->input_task_runner(),
     65       host_context_->ui_task_runner()));
     66 
     67   // Start monitoring configured policies.
     68   policy_watcher_.reset(
     69       policy_hack::PolicyWatcher::Create(host_context_->network_task_runner()));
     70   policy_watcher_->StartWatching(
     71       base::Bind(&It2MeHost::OnPolicyUpdate, this));
     72 
     73   // Switch to the network thread to start the actual connection.
     74   host_context_->network_task_runner()->PostTask(
     75       FROM_HERE, base::Bind(&It2MeHost::ReadPolicyAndConnect, this));
     76 }
     77 
     78 void It2MeHost::Disconnect() {
     79   if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
     80     DCHECK(task_runner_->BelongsToCurrentThread());
     81     host_context_->network_task_runner()->PostTask(
     82         FROM_HERE, base::Bind(&It2MeHost::Disconnect, this));
     83     return;
     84   }
     85 
     86   switch (state_) {
     87     case kDisconnected:
     88       ShutdownOnNetworkThread();
     89       return;
     90 
     91     case kStarting:
     92       SetState(kDisconnecting);
     93       SetState(kDisconnected);
     94       ShutdownOnNetworkThread();
     95       return;
     96 
     97     case kDisconnecting:
     98       return;
     99 
    100     default:
    101       SetState(kDisconnecting);
    102 
    103       if (!host_) {
    104         SetState(kDisconnected);
    105         ShutdownOnNetworkThread();
    106         return;
    107       }
    108 
    109       // Deleting the host destroys SignalStrategy synchronously, but
    110       // SignalStrategy::Listener handlers are not allowed to destroy
    111       // SignalStrategy, so post task to destroy the host later.
    112       host_context_->network_task_runner()->PostTask(
    113           FROM_HERE, base::Bind(&It2MeHost::ShutdownOnNetworkThread, this));
    114       return;
    115   }
    116 }
    117 
    118 void It2MeHost::RequestNatPolicy() {
    119   if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
    120     DCHECK(task_runner_->BelongsToCurrentThread());
    121     host_context_->network_task_runner()->PostTask(
    122         FROM_HERE, base::Bind(&It2MeHost::RequestNatPolicy, this));
    123     return;
    124   }
    125 
    126   if (policy_received_)
    127     UpdateNatPolicy(nat_traversal_enabled_);
    128 }
    129 
    130 void It2MeHost::ReadPolicyAndConnect() {
    131   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    132 
    133   SetState(kStarting);
    134 
    135   // Only proceed to FinishConnect() if at least one policy update has been
    136   // received.
    137   if (policy_received_) {
    138     FinishConnect();
    139   } else {
    140     // Otherwise, create the policy watcher, and thunk the connect.
    141     pending_connect_ =
    142         base::Bind(&It2MeHost::FinishConnect, this);
    143   }
    144 }
    145 
    146 void It2MeHost::FinishConnect() {
    147   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    148 
    149   if (state_ != kStarting) {
    150     // Host has been stopped while we were fetching policy.
    151     return;
    152   }
    153 
    154   // Check the host domain policy.
    155   if (!required_host_domain_.empty() &&
    156       !EndsWith(xmpp_server_config_.username,
    157                 std::string("@") + required_host_domain_, false)) {
    158     SetState(kInvalidDomainError);
    159     return;
    160   }
    161 
    162   // Generate a key pair for the Host to use.
    163   // TODO(wez): Move this to the worker thread.
    164   host_key_pair_ = RsaKeyPair::Generate();
    165 
    166   // Create XMPP connection.
    167   scoped_ptr<SignalStrategy> signal_strategy(
    168       new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
    169                              host_context_->url_request_context_getter(),
    170                              xmpp_server_config_));
    171 
    172   // Request registration of the host for support.
    173   scoped_ptr<RegisterSupportHostRequest> register_request(
    174       new RegisterSupportHostRequest(
    175           signal_strategy.get(), host_key_pair_, directory_bot_jid_,
    176           base::Bind(&It2MeHost::OnReceivedSupportID,
    177                      base::Unretained(this))));
    178 
    179   // Beyond this point nothing can fail, so save the config and request.
    180   signal_strategy_ = signal_strategy.Pass();
    181   register_request_ = register_request.Pass();
    182 
    183   // If NAT traversal is off then limit port range to allow firewall pin-holing.
    184   HOST_LOG << "NAT state: " << nat_traversal_enabled_;
    185   NetworkSettings network_settings(
    186      nat_traversal_enabled_ ?
    187      NetworkSettings::NAT_TRAVERSAL_ENABLED :
    188      NetworkSettings::NAT_TRAVERSAL_DISABLED);
    189   if (!nat_traversal_enabled_) {
    190     network_settings.min_port = NetworkSettings::kDefaultMinPort;
    191     network_settings.max_port = NetworkSettings::kDefaultMaxPort;
    192   }
    193 
    194   // Create the host.
    195   host_.reset(new ChromotingHost(
    196       signal_strategy_.get(),
    197       desktop_environment_factory_.get(),
    198       CreateHostSessionManager(signal_strategy_.get(), network_settings,
    199                                host_context_->url_request_context_getter()),
    200       host_context_->audio_task_runner(),
    201       host_context_->input_task_runner(),
    202       host_context_->video_capture_task_runner(),
    203       host_context_->video_encode_task_runner(),
    204       host_context_->network_task_runner(),
    205       host_context_->ui_task_runner()));
    206   host_->AddStatusObserver(this);
    207   log_to_server_.reset(
    208       new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
    209                       signal_strategy_.get(), directory_bot_jid_));
    210 
    211   // Disable audio by default.
    212   // TODO(sergeyu): Add UI to enable it.
    213   scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
    214       protocol::CandidateSessionConfig::CreateDefault();
    215   protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get());
    216 
    217   // VP9 encode is not yet supported.
    218   protocol::CandidateSessionConfig::DisableVideoCodec(
    219       protocol_config.get(), protocol::ChannelConfig::CODEC_VP9);
    220 
    221   host_->set_protocol_config(protocol_config.Pass());
    222 
    223   // Create event logger.
    224   host_event_logger_ =
    225       HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
    226 
    227   // Connect signaling and start the host.
    228   signal_strategy_->Connect();
    229   host_->Start(xmpp_server_config_.username);
    230 
    231   SetState(kRequestedAccessCode);
    232   return;
    233 }
    234 
    235 void It2MeHost::ShutdownOnNetworkThread() {
    236   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    237   DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
    238 
    239   if (state_ == kDisconnecting) {
    240     host_event_logger_.reset();
    241     host_->RemoveStatusObserver(this);
    242     host_.reset();
    243 
    244     register_request_.reset();
    245     log_to_server_.reset();
    246     signal_strategy_.reset();
    247     SetState(kDisconnected);
    248   }
    249 
    250   host_context_->ui_task_runner()->PostTask(
    251       FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
    252 }
    253 
    254 void It2MeHost::ShutdownOnUiThread() {
    255   DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
    256 
    257   // Destroy the DesktopEnvironmentFactory, to free thread references.
    258   desktop_environment_factory_.reset();
    259 
    260   // Stop listening for policy updates.
    261   if (policy_watcher_.get()) {
    262     base::WaitableEvent policy_watcher_stopped_(true, false);
    263     policy_watcher_->StopWatching(&policy_watcher_stopped_);
    264     policy_watcher_stopped_.Wait();
    265     policy_watcher_.reset();
    266   }
    267 }
    268 
    269 void It2MeHost::OnAccessDenied(const std::string& jid) {
    270   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    271 
    272   ++failed_login_attempts_;
    273   if (failed_login_attempts_ == kMaxLoginAttempts) {
    274     Disconnect();
    275   }
    276 }
    277 
    278 void It2MeHost::OnClientAuthenticated(const std::string& jid) {
    279   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    280 
    281   if (state_ == kDisconnecting) {
    282     // Ignore the new connection if we are disconnecting.
    283     return;
    284   }
    285   if (state_ == kConnected) {
    286     // If we already connected another client then one of the connections may be
    287     // an attacker, so both are suspect and we have to reject the second
    288     // connection and shutdown the host.
    289     host_->RejectAuthenticatingClient();
    290     Disconnect();
    291     return;
    292   }
    293 
    294   std::string client_username = jid;
    295   size_t pos = client_username.find('/');
    296   if (pos != std::string::npos)
    297     client_username.replace(pos, std::string::npos, "");
    298 
    299   HOST_LOG << "Client " << client_username << " connected.";
    300 
    301   // Pass the client user name to the script object before changing state.
    302   task_runner_->PostTask(
    303       FROM_HERE, base::Bind(&It2MeHost::Observer::OnClientAuthenticated,
    304                             observer_, client_username));
    305 
    306   SetState(kConnected);
    307 }
    308 
    309 void It2MeHost::OnClientDisconnected(const std::string& jid) {
    310   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    311 
    312   Disconnect();
    313 }
    314 
    315 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
    316   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    317 
    318   bool nat_policy;
    319   if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
    320                            &nat_policy)) {
    321     UpdateNatPolicy(nat_policy);
    322   }
    323   std::string host_domain;
    324   if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
    325                           &host_domain)) {
    326     UpdateHostDomainPolicy(host_domain);
    327   }
    328 
    329   policy_received_ = true;
    330 
    331   if (!pending_connect_.is_null()) {
    332     pending_connect_.Run();
    333     pending_connect_.Reset();
    334   }
    335 }
    336 
    337 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
    338   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    339 
    340   VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
    341 
    342   // When transitioning from enabled to disabled, force disconnect any
    343   // existing session.
    344   if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
    345     Disconnect();
    346   }
    347 
    348   nat_traversal_enabled_ = nat_traversal_enabled;
    349 
    350   // Notify the web-app of the policy setting.
    351   task_runner_->PostTask(
    352       FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
    353                             observer_, nat_traversal_enabled_));
    354 }
    355 
    356 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
    357   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    358 
    359   VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
    360 
    361   // When setting a host domain policy, force disconnect any existing session.
    362   if (!host_domain.empty() && IsConnected()) {
    363     Disconnect();
    364   }
    365 
    366   required_host_domain_ = host_domain;
    367 }
    368 
    369 It2MeHost::~It2MeHost() {
    370   // Check that resources that need to be torn down on the UI thread are gone.
    371   DCHECK(!desktop_environment_factory_.get());
    372   DCHECK(!policy_watcher_.get());
    373 }
    374 
    375 void It2MeHost::SetState(It2MeHostState state) {
    376   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    377 
    378   switch (state_) {
    379     case kDisconnected:
    380       DCHECK(state == kStarting ||
    381              state == kError) << state;
    382       break;
    383     case kStarting:
    384       DCHECK(state == kRequestedAccessCode ||
    385              state == kDisconnecting ||
    386              state == kError ||
    387              state == kInvalidDomainError) << state;
    388       break;
    389     case kRequestedAccessCode:
    390       DCHECK(state == kReceivedAccessCode ||
    391              state == kDisconnecting ||
    392              state == kError) << state;
    393       break;
    394     case kReceivedAccessCode:
    395       DCHECK(state == kConnected ||
    396              state == kDisconnecting ||
    397              state == kError) << state;
    398       break;
    399     case kConnected:
    400       DCHECK(state == kDisconnecting ||
    401              state == kDisconnected ||
    402              state == kError) << state;
    403       break;
    404     case kDisconnecting:
    405       DCHECK(state == kDisconnected) << state;
    406       break;
    407     case kError:
    408       DCHECK(state == kDisconnecting) << state;
    409       break;
    410     case kInvalidDomainError:
    411       DCHECK(state == kDisconnecting) << state;
    412       break;
    413   };
    414 
    415   state_ = state;
    416 
    417   // Post a state-change notification to the web-app.
    418   task_runner_->PostTask(
    419       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
    420                             observer_, state));
    421 }
    422 
    423 bool It2MeHost::IsConnected() const {
    424   return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
    425       state_ == kConnected;
    426 }
    427 
    428 void It2MeHost::OnReceivedSupportID(
    429     bool success,
    430     const std::string& support_id,
    431     const base::TimeDelta& lifetime) {
    432   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
    433 
    434   if (!success) {
    435     SetState(kError);
    436     Disconnect();
    437     return;
    438   }
    439 
    440   std::string host_secret = GenerateSupportHostSecret();
    441   std::string access_code = support_id + host_secret;
    442 
    443   std::string local_certificate = host_key_pair_->GenerateCertificate();
    444   if (local_certificate.empty()) {
    445     LOG(ERROR) << "Failed to generate host certificate.";
    446     SetState(kError);
    447     Disconnect();
    448     return;
    449   }
    450 
    451   scoped_ptr<protocol::AuthenticatorFactory> factory(
    452       new protocol::It2MeHostAuthenticatorFactory(
    453           local_certificate, host_key_pair_, access_code));
    454   host_->SetAuthenticatorFactory(factory.Pass());
    455 
    456   // Pass the Access Code to the script object before changing state.
    457   task_runner_->PostTask(
    458       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
    459                             observer_, access_code, lifetime));
    460 
    461   SetState(kReceivedAccessCode);
    462 }
    463 
    464 It2MeHostFactory::It2MeHostFactory() {}
    465 
    466 It2MeHostFactory::~It2MeHostFactory() {}
    467 
    468 scoped_refptr<It2MeHost> It2MeHostFactory::CreateIt2MeHost(
    469     ChromotingHostContext* context,
    470     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
    471     base::WeakPtr<It2MeHost::Observer> observer,
    472     const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
    473     const std::string& directory_bot_jid) {
    474   return new It2MeHost(
    475       context, task_runner, observer, xmpp_server_config, directory_bot_jid);
    476 }
    477 
    478 }  // namespace remoting
    479