Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / host / it2me / it2me_host.cc
blobfe9e8c6176a0d4f00188e1bcca28ad17984ad8b4
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.
5 #include "remoting/host/it2me/it2me_host.h"
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/strings/string_util.h"
10 #include "base/threading/platform_thread.h"
11 #include "net/socket/client_socket_factory.h"
12 #include "policy/policy_constants.h"
13 #include "remoting/base/auto_thread.h"
14 #include "remoting/base/logging.h"
15 #include "remoting/base/rsa_key_pair.h"
16 #include "remoting/host/chromoting_host.h"
17 #include "remoting/host/chromoting_host_context.h"
18 #include "remoting/host/host_event_logger.h"
19 #include "remoting/host/host_secret.h"
20 #include "remoting/host/host_status_logger.h"
21 #include "remoting/host/it2me/it2me_confirmation_dialog.h"
22 #include "remoting/host/it2me_desktop_environment.h"
23 #include "remoting/host/policy_watcher.h"
24 #include "remoting/host/register_support_host_request.h"
25 #include "remoting/host/session_manager_factory.h"
26 #include "remoting/protocol/it2me_host_authenticator_factory.h"
27 #include "remoting/protocol/network_settings.h"
28 #include "remoting/signaling/server_log_entry.h"
30 namespace remoting {
32 namespace {
34 // This is used for tagging system event logs.
35 const char kApplicationName[] = "chromoting";
36 const int kMaxLoginAttempts = 5;
38 } // namespace
40 It2MeHost::It2MeHost(
41 scoped_ptr<ChromotingHostContext> host_context,
42 scoped_ptr<PolicyWatcher> policy_watcher,
43 scoped_ptr<It2MeConfirmationDialogFactory> confirmation_dialog_factory,
44 base::WeakPtr<It2MeHost::Observer> observer,
45 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
46 const std::string& directory_bot_jid)
47 : host_context_(host_context.Pass()),
48 task_runner_(host_context_->ui_task_runner()),
49 observer_(observer),
50 xmpp_server_config_(xmpp_server_config),
51 directory_bot_jid_(directory_bot_jid),
52 state_(kDisconnected),
53 failed_login_attempts_(0),
54 policy_watcher_(policy_watcher.Pass()),
55 confirmation_dialog_factory_(confirmation_dialog_factory.Pass()),
56 nat_traversal_enabled_(false),
57 policy_received_(false) {
58 DCHECK(task_runner_->BelongsToCurrentThread());
61 void It2MeHost::Connect() {
62 if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
63 DCHECK(task_runner_->BelongsToCurrentThread());
64 host_context_->ui_task_runner()->PostTask(
65 FROM_HERE, base::Bind(&It2MeHost::Connect, this));
66 return;
69 desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
70 host_context_->network_task_runner(),
71 host_context_->input_task_runner(),
72 host_context_->ui_task_runner()));
74 // Start monitoring configured policies.
75 policy_watcher_->StartWatching(
76 base::Bind(&It2MeHost::OnPolicyUpdate, this),
77 base::Bind(&It2MeHost::OnPolicyError, this));
79 // Switch to the network thread to start the actual connection.
80 host_context_->network_task_runner()->PostTask(
81 FROM_HERE, base::Bind(&It2MeHost::ShowConfirmationPrompt, this));
84 void It2MeHost::Disconnect() {
85 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
86 DCHECK(task_runner_->BelongsToCurrentThread());
87 host_context_->network_task_runner()->PostTask(
88 FROM_HERE, base::Bind(&It2MeHost::Disconnect, this));
89 return;
92 switch (state_) {
93 case kDisconnected:
94 ShutdownOnNetworkThread();
95 return;
97 case kStarting:
98 SetState(kDisconnecting, "");
99 SetState(kDisconnected, "");
100 ShutdownOnNetworkThread();
101 return;
103 case kDisconnecting:
104 return;
106 default:
107 SetState(kDisconnecting, "");
109 if (!host_) {
110 SetState(kDisconnected, "");
111 ShutdownOnNetworkThread();
112 return;
115 // Deleting the host destroys SignalStrategy synchronously, but
116 // SignalStrategy::Listener handlers are not allowed to destroy
117 // SignalStrategy, so post task to destroy the host later.
118 host_context_->network_task_runner()->PostTask(
119 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnNetworkThread, this));
120 return;
124 void It2MeHost::RequestNatPolicy() {
125 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
126 DCHECK(task_runner_->BelongsToCurrentThread());
127 host_context_->network_task_runner()->PostTask(
128 FROM_HERE, base::Bind(&It2MeHost::RequestNatPolicy, this));
129 return;
132 if (policy_received_)
133 UpdateNatPolicy(nat_traversal_enabled_);
136 void It2MeHost::ShowConfirmationPrompt() {
137 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
139 SetState(kStarting, "");
141 scoped_ptr<It2MeConfirmationDialog> confirmation_dialog =
142 confirmation_dialog_factory_->Create();
144 // TODO(dcaiafa): Remove after dialog implementations for all platforms exist.
145 if (!confirmation_dialog) {
146 ReadPolicyAndConnect();
147 return;
150 confirmation_dialog_proxy_.reset(
151 new It2MeConfirmationDialogProxy(host_context_->ui_task_runner(),
152 confirmation_dialog.Pass()));
154 confirmation_dialog_proxy_->Show(
155 base::Bind(&It2MeHost::OnConfirmationResult, base::Unretained(this)));
158 void It2MeHost::OnConfirmationResult(It2MeConfirmationDialog::Result result) {
159 switch (result) {
160 case It2MeConfirmationDialog::Result::OK:
161 ReadPolicyAndConnect();
162 break;
164 case It2MeConfirmationDialog::Result::CANCEL:
165 Disconnect();
166 break;
168 default:
169 NOTREACHED();
170 return;
174 void It2MeHost::ReadPolicyAndConnect() {
175 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
176 DCHECK_EQ(kStarting, state_);
178 // Only proceed to FinishConnect() if at least one policy update has been
179 // received.
180 if (policy_received_) {
181 FinishConnect();
182 } else {
183 // Otherwise, create the policy watcher, and thunk the connect.
184 pending_connect_ =
185 base::Bind(&It2MeHost::FinishConnect, this);
189 void It2MeHost::FinishConnect() {
190 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
192 if (state_ != kStarting) {
193 // Host has been stopped while we were fetching policy.
194 return;
197 // Check the host domain policy.
198 if (!required_host_domain_.empty() &&
199 !base::EndsWith(xmpp_server_config_.username,
200 std::string("@") + required_host_domain_,
201 base::CompareCase::INSENSITIVE_ASCII)) {
202 SetState(kInvalidDomainError, "");
203 return;
206 // Generate a key pair for the Host to use.
207 // TODO(wez): Move this to the worker thread.
208 host_key_pair_ = RsaKeyPair::Generate();
210 // Create XMPP connection.
211 scoped_ptr<SignalStrategy> signal_strategy(
212 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
213 host_context_->url_request_context_getter(),
214 xmpp_server_config_));
216 // Request registration of the host for support.
217 scoped_ptr<RegisterSupportHostRequest> register_request(
218 new RegisterSupportHostRequest(
219 signal_strategy.get(), host_key_pair_, directory_bot_jid_,
220 base::Bind(&It2MeHost::OnReceivedSupportID,
221 base::Unretained(this))));
223 // Beyond this point nothing can fail, so save the config and request.
224 signal_strategy_ = signal_strategy.Pass();
225 register_request_ = register_request.Pass();
227 // If NAT traversal is off then limit port range to allow firewall pin-holing.
228 HOST_LOG << "NAT state: " << nat_traversal_enabled_;
229 protocol::NetworkSettings network_settings(
230 nat_traversal_enabled_ ?
231 protocol::NetworkSettings::NAT_TRAVERSAL_FULL :
232 protocol::NetworkSettings::NAT_TRAVERSAL_DISABLED);
233 if (!nat_traversal_enabled_) {
234 network_settings.port_range.min_port =
235 protocol::NetworkSettings::kDefaultMinPort;
236 network_settings.port_range.max_port =
237 protocol::NetworkSettings::kDefaultMaxPort;
240 // Create the host.
241 host_.reset(new ChromotingHost(
242 signal_strategy_.get(),
243 desktop_environment_factory_.get(),
244 CreateHostSessionManager(signal_strategy_.get(), network_settings,
245 host_context_->url_request_context_getter()),
246 host_context_->audio_task_runner(),
247 host_context_->input_task_runner(),
248 host_context_->video_capture_task_runner(),
249 host_context_->video_encode_task_runner(),
250 host_context_->network_task_runner(),
251 host_context_->ui_task_runner()));
252 host_->AddStatusObserver(this);
253 host_status_logger_.reset(
254 new HostStatusLogger(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
255 signal_strategy_.get(), directory_bot_jid_));
257 // Disable audio by default.
258 // TODO(sergeyu): Add UI to enable it.
259 scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
260 protocol::CandidateSessionConfig::CreateDefault();
261 protocol_config->DisableAudioChannel();
263 host_->set_protocol_config(protocol_config.Pass());
265 // Create event logger.
266 host_event_logger_ =
267 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
269 // Connect signaling and start the host.
270 signal_strategy_->Connect();
271 host_->Start(xmpp_server_config_.username);
273 SetState(kRequestedAccessCode, "");
274 return;
277 void It2MeHost::ShutdownOnNetworkThread() {
278 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
279 DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
281 confirmation_dialog_proxy_.reset();
283 if (state_ == kDisconnecting) {
284 host_event_logger_.reset();
285 host_->RemoveStatusObserver(this);
286 host_.reset();
288 register_request_.reset();
289 host_status_logger_.reset();
290 signal_strategy_.reset();
291 SetState(kDisconnected, "");
294 host_context_->ui_task_runner()->PostTask(
295 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
298 void It2MeHost::ShutdownOnUiThread() {
299 DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
301 // Destroy the DesktopEnvironmentFactory, to free thread references.
302 desktop_environment_factory_.reset();
304 // Stop listening for policy updates.
305 policy_watcher_.reset();
308 void It2MeHost::OnAccessDenied(const std::string& jid) {
309 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
311 ++failed_login_attempts_;
312 if (failed_login_attempts_ == kMaxLoginAttempts) {
313 Disconnect();
317 void It2MeHost::OnClientAuthenticated(const std::string& jid) {
318 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
320 if (state_ == kDisconnecting) {
321 // Ignore the new connection if we are disconnecting.
322 return;
324 if (state_ == kConnected) {
325 // If we already connected another client then one of the connections may be
326 // an attacker, so both are suspect and we have to reject the second
327 // connection and shutdown the host.
328 host_->RejectAuthenticatingClient();
329 Disconnect();
330 return;
333 std::string client_username = jid;
334 size_t pos = client_username.find('/');
335 if (pos != std::string::npos)
336 client_username.replace(pos, std::string::npos, "");
338 HOST_LOG << "Client " << client_username << " connected.";
340 // Pass the client user name to the script object before changing state.
341 task_runner_->PostTask(
342 FROM_HERE, base::Bind(&It2MeHost::Observer::OnClientAuthenticated,
343 observer_, client_username));
345 SetState(kConnected, "");
348 void It2MeHost::OnClientDisconnected(const std::string& jid) {
349 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
351 Disconnect();
354 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
355 // The policy watcher runs on the |ui_task_runner|.
356 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
357 host_context_->network_task_runner()->PostTask(
358 FROM_HERE,
359 base::Bind(&It2MeHost::OnPolicyUpdate, this, base::Passed(&policies)));
360 return;
363 bool nat_policy;
364 if (policies->GetBoolean(policy::key::kRemoteAccessHostFirewallTraversal,
365 &nat_policy)) {
366 UpdateNatPolicy(nat_policy);
368 std::string host_domain;
369 if (policies->GetString(policy::key::kRemoteAccessHostDomain, &host_domain)) {
370 UpdateHostDomainPolicy(host_domain);
373 policy_received_ = true;
375 if (!pending_connect_.is_null()) {
376 base::ResetAndReturn(&pending_connect_).Run();
380 void It2MeHost::OnPolicyError() {
381 // TODO(lukasza): Report the policy error to the user. crbug.com/433009
382 NOTIMPLEMENTED();
385 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
386 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
388 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
390 // When transitioning from enabled to disabled, force disconnect any
391 // existing session.
392 if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
393 Disconnect();
396 nat_traversal_enabled_ = nat_traversal_enabled;
398 // Notify the web-app of the policy setting.
399 task_runner_->PostTask(
400 FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
401 observer_, nat_traversal_enabled_));
404 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
405 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
407 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
409 // When setting a host domain policy, force disconnect any existing session.
410 if (!host_domain.empty() && IsConnected()) {
411 Disconnect();
414 required_host_domain_ = host_domain;
417 It2MeHost::~It2MeHost() {
418 // Check that resources that need to be torn down on the UI thread are gone.
419 DCHECK(!desktop_environment_factory_.get());
420 DCHECK(!policy_watcher_.get());
423 void It2MeHost::SetState(It2MeHostState state,
424 const std::string& error_message) {
425 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
427 switch (state_) {
428 case kDisconnected:
429 DCHECK(state == kStarting ||
430 state == kError) << state;
431 break;
432 case kStarting:
433 DCHECK(state == kRequestedAccessCode ||
434 state == kDisconnecting ||
435 state == kError ||
436 state == kInvalidDomainError) << state;
437 break;
438 case kRequestedAccessCode:
439 DCHECK(state == kReceivedAccessCode ||
440 state == kDisconnecting ||
441 state == kError) << state;
442 break;
443 case kReceivedAccessCode:
444 DCHECK(state == kConnected ||
445 state == kDisconnecting ||
446 state == kError) << state;
447 break;
448 case kConnected:
449 DCHECK(state == kDisconnecting ||
450 state == kDisconnected ||
451 state == kError) << state;
452 break;
453 case kDisconnecting:
454 DCHECK(state == kDisconnected) << state;
455 break;
456 case kError:
457 DCHECK(state == kDisconnecting) << state;
458 break;
459 case kInvalidDomainError:
460 DCHECK(state == kDisconnecting) << state;
461 break;
464 state_ = state;
466 // Post a state-change notification to the web-app.
467 task_runner_->PostTask(
468 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
469 observer_, state, error_message));
472 bool It2MeHost::IsConnected() const {
473 return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
474 state_ == kConnected;
477 void It2MeHost::OnReceivedSupportID(
478 const std::string& support_id,
479 const base::TimeDelta& lifetime,
480 const std::string& error_message) {
481 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
483 if (!error_message.empty()) {
484 SetState(kError, error_message);
485 Disconnect();
486 return;
489 std::string host_secret = GenerateSupportHostSecret();
490 std::string access_code = support_id + host_secret;
492 std::string local_certificate = host_key_pair_->GenerateCertificate();
493 if (local_certificate.empty()) {
494 std::string error_message = "Failed to generate host certificate.";
495 LOG(ERROR) << error_message;
496 SetState(kError, error_message);
497 Disconnect();
498 return;
501 scoped_ptr<protocol::AuthenticatorFactory> factory(
502 new protocol::It2MeHostAuthenticatorFactory(
503 local_certificate, host_key_pair_, access_code));
504 host_->SetAuthenticatorFactory(factory.Pass());
506 // Pass the Access Code to the script object before changing state.
507 task_runner_->PostTask(
508 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
509 observer_, access_code, lifetime));
511 SetState(kReceivedAccessCode, "");
514 It2MeHostFactory::It2MeHostFactory() : policy_service_(nullptr) {
517 It2MeHostFactory::~It2MeHostFactory() {}
519 void It2MeHostFactory::set_policy_service(
520 policy::PolicyService* policy_service) {
521 DCHECK(policy_service);
522 DCHECK(!policy_service_) << "|policy_service| can only be set once.";
523 policy_service_ = policy_service;
526 scoped_refptr<It2MeHost> It2MeHostFactory::CreateIt2MeHost(
527 scoped_ptr<ChromotingHostContext> context,
528 base::WeakPtr<It2MeHost::Observer> observer,
529 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
530 const std::string& directory_bot_jid) {
531 DCHECK(context->ui_task_runner()->BelongsToCurrentThread());
533 scoped_ptr<It2MeConfirmationDialogFactory> confirmation_dialog_factory(
534 new It2MeConfirmationDialogFactory());
535 scoped_ptr<PolicyWatcher> policy_watcher =
536 PolicyWatcher::Create(policy_service_, context->file_task_runner());
537 return new It2MeHost(context.Pass(), policy_watcher.Pass(),
538 confirmation_dialog_factory.Pass(),
539 observer, xmpp_server_config, directory_bot_jid);
542 } // namespace remoting