Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / remoting / host / it2me / it2me_host.cc
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/threading/platform_thread.h"
10 #include "net/socket/client_socket_factory.h"
11 #include "remoting/base/auto_thread.h"
12 #include "remoting/base/logging.h"
13 #include "remoting/base/rsa_key_pair.h"
14 #include "remoting/host/chromoting_host.h"
15 #include "remoting/host/chromoting_host_context.h"
16 #include "remoting/host/host_event_logger.h"
17 #include "remoting/host/host_secret.h"
18 #include "remoting/host/host_status_logger.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/protocol/it2me_host_authenticator_factory.h"
24 #include "remoting/protocol/network_settings.h"
25 #include "remoting/signaling/server_log_entry.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     scoped_ptr<ChromotingHostContext> host_context,
39     scoped_ptr<policy_hack::PolicyWatcher> policy_watcher,
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.Pass()),
44     task_runner_(host_context_->ui_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     policy_watcher_(policy_watcher.Pass()),
51     nat_traversal_enabled_(false),
52     policy_received_(false) {
53   DCHECK(task_runner_->BelongsToCurrentThread());
54 }
55
56 void 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_->StartWatching(base::Bind(&It2MeHost::OnPolicyUpdate, this));
71
72   // Switch to the network thread to start the actual connection.
73   host_context_->network_task_runner()->PostTask(
74       FROM_HERE, base::Bind(&It2MeHost::ReadPolicyAndConnect, this));
75 }
76
77 void It2MeHost::Disconnect() {
78   if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
79     DCHECK(task_runner_->BelongsToCurrentThread());
80     host_context_->network_task_runner()->PostTask(
81         FROM_HERE, base::Bind(&It2MeHost::Disconnect, this));
82     return;
83   }
84
85   switch (state_) {
86     case kDisconnected:
87       ShutdownOnNetworkThread();
88       return;
89
90     case kStarting:
91       SetState(kDisconnecting);
92       SetState(kDisconnected);
93       ShutdownOnNetworkThread();
94       return;
95
96     case kDisconnecting:
97       return;
98
99     default:
100       SetState(kDisconnecting);
101
102       if (!host_) {
103         SetState(kDisconnected);
104         ShutdownOnNetworkThread();
105         return;
106       }
107
108       // Deleting the host destroys SignalStrategy synchronously, but
109       // SignalStrategy::Listener handlers are not allowed to destroy
110       // SignalStrategy, so post task to destroy the host later.
111       host_context_->network_task_runner()->PostTask(
112           FROM_HERE, base::Bind(&It2MeHost::ShutdownOnNetworkThread, this));
113       return;
114   }
115 }
116
117 void It2MeHost::RequestNatPolicy() {
118   if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
119     DCHECK(task_runner_->BelongsToCurrentThread());
120     host_context_->network_task_runner()->PostTask(
121         FROM_HERE, base::Bind(&It2MeHost::RequestNatPolicy, this));
122     return;
123   }
124
125   if (policy_received_)
126     UpdateNatPolicy(nat_traversal_enabled_);
127 }
128
129 void It2MeHost::ReadPolicyAndConnect() {
130   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
131
132   SetState(kStarting);
133
134   // Only proceed to FinishConnect() if at least one policy update has been
135   // received.
136   if (policy_received_) {
137     FinishConnect();
138   } else {
139     // Otherwise, create the policy watcher, and thunk the connect.
140     pending_connect_ =
141         base::Bind(&It2MeHost::FinishConnect, this);
142   }
143 }
144
145 void It2MeHost::FinishConnect() {
146   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
147
148   if (state_ != kStarting) {
149     // Host has been stopped while we were fetching policy.
150     return;
151   }
152
153   // Check the host domain policy.
154   if (!required_host_domain_.empty() &&
155       !EndsWith(xmpp_server_config_.username,
156                 std::string("@") + required_host_domain_, false)) {
157     SetState(kInvalidDomainError);
158     return;
159   }
160
161   // Generate a key pair for the Host to use.
162   // TODO(wez): Move this to the worker thread.
163   host_key_pair_ = RsaKeyPair::Generate();
164
165   // Create XMPP connection.
166   scoped_ptr<SignalStrategy> signal_strategy(
167       new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
168                              host_context_->url_request_context_getter(),
169                              xmpp_server_config_));
170
171   // Request registration of the host for support.
172   scoped_ptr<RegisterSupportHostRequest> register_request(
173       new RegisterSupportHostRequest(
174           signal_strategy.get(), host_key_pair_, directory_bot_jid_,
175           base::Bind(&It2MeHost::OnReceivedSupportID,
176                      base::Unretained(this))));
177
178   // Beyond this point nothing can fail, so save the config and request.
179   signal_strategy_ = signal_strategy.Pass();
180   register_request_ = register_request.Pass();
181
182   // If NAT traversal is off then limit port range to allow firewall pin-holing.
183   HOST_LOG << "NAT state: " << nat_traversal_enabled_;
184   protocol::NetworkSettings network_settings(
185      nat_traversal_enabled_ ?
186      protocol::NetworkSettings::NAT_TRAVERSAL_FULL :
187      protocol::NetworkSettings::NAT_TRAVERSAL_DISABLED);
188   if (!nat_traversal_enabled_) {
189     network_settings.min_port = protocol::NetworkSettings::kDefaultMinPort;
190     network_settings.max_port = protocol::NetworkSettings::kDefaultMaxPort;
191   }
192
193   // Create the host.
194   host_.reset(new ChromotingHost(
195       signal_strategy_.get(),
196       desktop_environment_factory_.get(),
197       CreateHostSessionManager(signal_strategy_.get(), network_settings,
198                                host_context_->url_request_context_getter()),
199       host_context_->audio_task_runner(),
200       host_context_->input_task_runner(),
201       host_context_->video_capture_task_runner(),
202       host_context_->video_encode_task_runner(),
203       host_context_->network_task_runner(),
204       host_context_->ui_task_runner()));
205   host_->AddStatusObserver(this);
206   host_status_logger_.reset(
207       new HostStatusLogger(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
208                            signal_strategy_.get(), directory_bot_jid_));
209
210   // Disable audio by default.
211   // TODO(sergeyu): Add UI to enable it.
212   scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
213       protocol::CandidateSessionConfig::CreateDefault();
214   protocol_config->DisableAudioChannel();
215
216   host_->set_protocol_config(protocol_config.Pass());
217
218   // Create event logger.
219   host_event_logger_ =
220       HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
221
222   // Connect signaling and start the host.
223   signal_strategy_->Connect();
224   host_->Start(xmpp_server_config_.username);
225
226   SetState(kRequestedAccessCode);
227   return;
228 }
229
230 void It2MeHost::ShutdownOnNetworkThread() {
231   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
232   DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
233
234   if (state_ == kDisconnecting) {
235     host_event_logger_.reset();
236     host_->RemoveStatusObserver(this);
237     host_.reset();
238
239     register_request_.reset();
240     host_status_logger_.reset();
241     signal_strategy_.reset();
242     SetState(kDisconnected);
243   }
244
245   host_context_->ui_task_runner()->PostTask(
246       FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
247 }
248
249 void It2MeHost::ShutdownOnUiThread() {
250   DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
251
252   // Destroy the DesktopEnvironmentFactory, to free thread references.
253   desktop_environment_factory_.reset();
254
255   // Stop listening for policy updates.
256   if (policy_watcher_.get()) {
257     policy_watcher_->StopWatching(
258         base::Bind(&It2MeHost::OnPolicyWatcherShutdown, this));
259     return;
260   }
261 }
262
263 void It2MeHost::OnPolicyWatcherShutdown() {
264   policy_watcher_.reset();
265 }
266
267 void 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
276 void 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
307 void It2MeHost::OnClientDisconnected(const std::string& jid) {
308   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
309
310   Disconnect();
311 }
312
313 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
314   // The policy watcher runs on the |ui_task_runner| on ChromeOS and the
315   // |network_task_runner| on other platforms.
316   if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
317     host_context_->network_task_runner()->PostTask(
318         FROM_HERE,
319         base::Bind(&It2MeHost::OnPolicyUpdate, this, base::Passed(&policies)));
320     return;
321   }
322
323   bool nat_policy;
324   if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
325                            &nat_policy)) {
326     UpdateNatPolicy(nat_policy);
327   }
328   std::string host_domain;
329   if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
330                           &host_domain)) {
331     UpdateHostDomainPolicy(host_domain);
332   }
333
334   policy_received_ = true;
335
336   if (!pending_connect_.is_null()) {
337     pending_connect_.Run();
338     pending_connect_.Reset();
339   }
340 }
341
342 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
343   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
344
345   VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
346
347   // When transitioning from enabled to disabled, force disconnect any
348   // existing session.
349   if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
350     Disconnect();
351   }
352
353   nat_traversal_enabled_ = nat_traversal_enabled;
354
355   // Notify the web-app of the policy setting.
356   task_runner_->PostTask(
357       FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
358                             observer_, nat_traversal_enabled_));
359 }
360
361 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
362   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
363
364   VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
365
366   // When setting a host domain policy, force disconnect any existing session.
367   if (!host_domain.empty() && IsConnected()) {
368     Disconnect();
369   }
370
371   required_host_domain_ = host_domain;
372 }
373
374 It2MeHost::~It2MeHost() {
375   // Check that resources that need to be torn down on the UI thread are gone.
376   DCHECK(!desktop_environment_factory_.get());
377   DCHECK(!policy_watcher_.get());
378 }
379
380 void It2MeHost::SetState(It2MeHostState state) {
381   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
382
383   switch (state_) {
384     case kDisconnected:
385       DCHECK(state == kStarting ||
386              state == kError) << state;
387       break;
388     case kStarting:
389       DCHECK(state == kRequestedAccessCode ||
390              state == kDisconnecting ||
391              state == kError ||
392              state == kInvalidDomainError) << state;
393       break;
394     case kRequestedAccessCode:
395       DCHECK(state == kReceivedAccessCode ||
396              state == kDisconnecting ||
397              state == kError) << state;
398       break;
399     case kReceivedAccessCode:
400       DCHECK(state == kConnected ||
401              state == kDisconnecting ||
402              state == kError) << state;
403       break;
404     case kConnected:
405       DCHECK(state == kDisconnecting ||
406              state == kDisconnected ||
407              state == kError) << state;
408       break;
409     case kDisconnecting:
410       DCHECK(state == kDisconnected) << state;
411       break;
412     case kError:
413       DCHECK(state == kDisconnecting) << state;
414       break;
415     case kInvalidDomainError:
416       DCHECK(state == kDisconnecting) << state;
417       break;
418   };
419
420   state_ = state;
421
422   // Post a state-change notification to the web-app.
423   task_runner_->PostTask(
424       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
425                             observer_, state));
426 }
427
428 bool It2MeHost::IsConnected() const {
429   return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
430       state_ == kConnected;
431 }
432
433 void It2MeHost::OnReceivedSupportID(
434     bool success,
435     const std::string& support_id,
436     const base::TimeDelta& lifetime) {
437   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
438
439   if (!success) {
440     SetState(kError);
441     Disconnect();
442     return;
443   }
444
445   std::string host_secret = GenerateSupportHostSecret();
446   std::string access_code = support_id + host_secret;
447
448   std::string local_certificate = host_key_pair_->GenerateCertificate();
449   if (local_certificate.empty()) {
450     LOG(ERROR) << "Failed to generate host certificate.";
451     SetState(kError);
452     Disconnect();
453     return;
454   }
455
456   scoped_ptr<protocol::AuthenticatorFactory> factory(
457       new protocol::It2MeHostAuthenticatorFactory(
458           local_certificate, host_key_pair_, access_code));
459   host_->SetAuthenticatorFactory(factory.Pass());
460
461   // Pass the Access Code to the script object before changing state.
462   task_runner_->PostTask(
463       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
464                             observer_, access_code, lifetime));
465
466   SetState(kReceivedAccessCode);
467 }
468
469 It2MeHostFactory::It2MeHostFactory() : policy_service_(nullptr) {
470 }
471
472 It2MeHostFactory::~It2MeHostFactory() {}
473
474 void It2MeHostFactory::set_policy_service(
475     policy::PolicyService* policy_service) {
476   DCHECK(policy_service);
477   DCHECK(!policy_service_) << "|policy_service| can only be set once.";
478   policy_service_ = policy_service;
479 }
480
481 scoped_refptr<It2MeHost> It2MeHostFactory::CreateIt2MeHost(
482     scoped_ptr<ChromotingHostContext> context,
483     base::WeakPtr<It2MeHost::Observer> observer,
484     const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
485     const std::string& directory_bot_jid) {
486   scoped_ptr<policy_hack::PolicyWatcher> policy_watcher =
487       policy_hack::PolicyWatcher::Create(policy_service_,
488                                          context->network_task_runner());
489   return new It2MeHost(context.Pass(), policy_watcher.Pass(), observer,
490                        xmpp_server_config, directory_bot_jid);
491 }
492
493 }  // namespace remoting