- add sources.
[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/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/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/it2me_desktop_environment.h"
19 #include "remoting/host/policy_hack/policy_watcher.h"
20 #include "remoting/host/register_support_host_request.h"
21 #include "remoting/host/session_manager_factory.h"
22 #include "remoting/jingle_glue/network_settings.h"
23 #include "remoting/protocol/it2me_host_authenticator_factory.h"
24
25 namespace remoting {
26
27 namespace {
28
29 // This is used for tagging system event logs.
30 const char kApplicationName[] = "chromoting";
31 const int kMaxLoginAttempts = 5;
32
33 }  // namespace
34
35 It2MeHost::It2MeHost(
36     scoped_ptr<ChromotingHostContext> host_context,
37     scoped_refptr<base::SingleThreadTaskRunner> plugin_task_runner,
38     base::WeakPtr<It2MeHost::Observer> observer,
39     const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
40     const std::string& directory_bot_jid)
41   : host_context_(host_context.Pass()),
42     plugin_task_runner_(plugin_task_runner),
43     observer_(observer),
44     xmpp_server_config_(xmpp_server_config),
45     directory_bot_jid_(directory_bot_jid),
46     state_(kDisconnected),
47     failed_login_attempts_(0),
48     nat_traversal_enabled_(false),
49     policy_received_(false) {
50   DCHECK(plugin_task_runner_->BelongsToCurrentThread());
51 }
52
53 void It2MeHost::Connect() {
54   if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
55     DCHECK(plugin_task_runner_->BelongsToCurrentThread());
56     host_context_->ui_task_runner()->PostTask(
57         FROM_HERE, base::Bind(&It2MeHost::Connect, this));
58     return;
59   }
60
61   desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
62       host_context_->network_task_runner(),
63       host_context_->input_task_runner(),
64       host_context_->ui_task_runner()));
65
66   // Start monitoring configured policies.
67   policy_watcher_.reset(
68       policy_hack::PolicyWatcher::Create(host_context_->network_task_runner()));
69   policy_watcher_->StartWatching(
70       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(plugin_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(plugin_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   LOG(INFO) << "NAT state: " << nat_traversal_enabled_;
184   NetworkSettings network_settings(
185      nat_traversal_enabled_ ?
186      NetworkSettings::NAT_TRAVERSAL_ENABLED :
187      NetworkSettings::NAT_TRAVERSAL_DISABLED);
188   if (!nat_traversal_enabled_) {
189     network_settings.min_port = NetworkSettings::kDefaultMinPort;
190     network_settings.max_port = NetworkSettings::kDefaultMaxPort;
191   }
192
193   // Create the host.
194   host_.reset(new ChromotingHost(
195       signal_strategy_.get(),
196       desktop_environment_factory_.get(),
197       CreateHostSessionManager(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   log_to_server_.reset(
207       new LogToServer(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::CandidateSessionConfig::DisableAudioChannel(protocol_config.get());
215
216   // VP9 encode is not yet supported.
217   protocol::CandidateSessionConfig::DisableVideoCodec(
218       protocol_config.get(), protocol::ChannelConfig::CODEC_VP9);
219
220   host_->set_protocol_config(protocol_config.Pass());
221
222   // Create event logger.
223   host_event_logger_ =
224       HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
225
226   // Connect signaling and start the host.
227   signal_strategy_->Connect();
228   host_->Start(xmpp_server_config_.username);
229
230   SetState(kRequestedAccessCode);
231   return;
232 }
233
234 void It2MeHost::ShutdownOnNetworkThread() {
235   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
236   DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
237
238   if (state_ == kDisconnecting) {
239     host_event_logger_.reset();
240     host_->RemoveStatusObserver(this);
241     host_.reset();
242
243     register_request_.reset();
244     log_to_server_.reset();
245     signal_strategy_.reset();
246     SetState(kDisconnected);
247   }
248
249   host_context_->ui_task_runner()->PostTask(
250       FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
251 }
252
253 void It2MeHost::ShutdownOnUiThread() {
254   DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
255
256   // Destroy the DesktopEnvironmentFactory, to free thread references.
257   desktop_environment_factory_.reset();
258
259   // Stop listening for policy updates.
260   if (policy_watcher_.get()) {
261     base::WaitableEvent policy_watcher_stopped_(true, false);
262     policy_watcher_->StopWatching(&policy_watcher_stopped_);
263     policy_watcher_stopped_.Wait();
264     policy_watcher_.reset();
265   }
266 }
267
268 void It2MeHost::OnAccessDenied(const std::string& jid) {
269   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
270
271   ++failed_login_attempts_;
272   if (failed_login_attempts_ == kMaxLoginAttempts) {
273     Disconnect();
274   }
275 }
276
277 void It2MeHost::OnClientAuthenticated(const std::string& jid) {
278   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
279
280   if (state_ == kDisconnecting) {
281     // Ignore the new connection if we are disconnecting.
282     return;
283   }
284   if (state_ == kConnected) {
285     // If we already connected another client then one of the connections may be
286     // an attacker, so both are suspect and we have to reject the second
287     // connection and shutdown the host.
288     host_->RejectAuthenticatingClient();
289     Disconnect();
290     return;
291   }
292
293   std::string client_username = jid;
294   size_t pos = client_username.find('/');
295   if (pos != std::string::npos)
296     client_username.replace(pos, std::string::npos, "");
297
298   LOG(INFO) << "Client " << client_username << " connected.";
299
300   // Pass the client user name to the script object before changing state.
301   plugin_task_runner_->PostTask(
302       FROM_HERE, base::Bind(&It2MeHost::Observer::OnClientAuthenticated,
303                             observer_, client_username));
304
305   SetState(kConnected);
306 }
307
308 void It2MeHost::OnClientDisconnected(const std::string& jid) {
309   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
310
311   Disconnect();
312 }
313
314 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
315   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
316
317   bool nat_policy;
318   if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
319                            &nat_policy)) {
320     UpdateNatPolicy(nat_policy);
321   }
322   std::string host_domain;
323   if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
324                           &host_domain)) {
325     UpdateHostDomainPolicy(host_domain);
326   }
327
328   policy_received_ = true;
329
330   if (!pending_connect_.is_null()) {
331     pending_connect_.Run();
332     pending_connect_.Reset();
333   }
334 }
335
336 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
337   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
338
339   VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
340
341   // When transitioning from enabled to disabled, force disconnect any
342   // existing session.
343   if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
344     Disconnect();
345   }
346
347   nat_traversal_enabled_ = nat_traversal_enabled;
348
349   // Notify the web-app of the policy setting.
350   plugin_task_runner_->PostTask(
351       FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
352                             observer_, nat_traversal_enabled_));
353 }
354
355 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
356   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
357
358   VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
359
360   // When setting a host domain policy, force disconnect any existing session.
361   if (!host_domain.empty() && IsConnected()) {
362     Disconnect();
363   }
364
365   required_host_domain_ = host_domain;
366 }
367
368 It2MeHost::~It2MeHost() {
369   // Check that resources that need to be torn down on the UI thread are gone.
370   DCHECK(!desktop_environment_factory_.get());
371   DCHECK(!policy_watcher_.get());
372 }
373
374 void It2MeHost::SetState(It2MeHostState state) {
375   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
376
377   switch (state_) {
378     case kDisconnected:
379       DCHECK(state == kStarting ||
380              state == kError) << state;
381       break;
382     case kStarting:
383       DCHECK(state == kRequestedAccessCode ||
384              state == kDisconnecting ||
385              state == kError ||
386              state == kInvalidDomainError) << state;
387       break;
388     case kRequestedAccessCode:
389       DCHECK(state == kReceivedAccessCode ||
390              state == kDisconnecting ||
391              state == kError) << state;
392       break;
393     case kReceivedAccessCode:
394       DCHECK(state == kConnected ||
395              state == kDisconnecting ||
396              state == kError) << state;
397       break;
398     case kConnected:
399       DCHECK(state == kDisconnecting ||
400              state == kDisconnected ||
401              state == kError) << state;
402       break;
403     case kDisconnecting:
404       DCHECK(state == kDisconnected) << state;
405       break;
406     case kError:
407       DCHECK(state == kDisconnecting) << state;
408       break;
409     case kInvalidDomainError:
410       DCHECK(state == kDisconnecting) << state;
411       break;
412   };
413
414   state_ = state;
415
416   // Post a state-change notification to the web-app.
417   plugin_task_runner_->PostTask(
418       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
419                             observer_, state));
420 }
421
422 bool It2MeHost::IsConnected() const {
423   return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
424       state_ == kConnected;
425 }
426
427 void It2MeHost::OnReceivedSupportID(
428     bool success,
429     const std::string& support_id,
430     const base::TimeDelta& lifetime) {
431   DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
432
433   if (!success) {
434     SetState(kError);
435     Disconnect();
436     return;
437   }
438
439   std::string host_secret = GenerateSupportHostSecret();
440   std::string access_code = support_id + host_secret;
441
442   std::string local_certificate = host_key_pair_->GenerateCertificate();
443   if (local_certificate.empty()) {
444     LOG(ERROR) << "Failed to generate host certificate.";
445     SetState(kError);
446     Disconnect();
447     return;
448   }
449
450   scoped_ptr<protocol::AuthenticatorFactory> factory(
451       new protocol::It2MeHostAuthenticatorFactory(
452           local_certificate, host_key_pair_, access_code));
453   host_->SetAuthenticatorFactory(factory.Pass());
454
455   // Pass the Access Code to the script object before changing state.
456   plugin_task_runner_->PostTask(
457       FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
458                             observer_, access_code, lifetime));
459
460   SetState(kReceivedAccessCode);
461 }
462
463 }  // namespace remoting