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