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