Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / remoting / host / it2me / it2me_host.cc
blob0ea715f3231449342b66acbac4b380b5e30e9ff4
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/strings/string_util.h"
9 #include "base/threading/platform_thread.h"
10 #include "net/socket/client_socket_factory.h"
11 #include "policy/policy_constants.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/it2me_confirmation_dialog.h"
21 #include "remoting/host/it2me_desktop_environment.h"
22 #include "remoting/host/policy_watcher.h"
23 #include "remoting/host/register_support_host_request.h"
24 #include "remoting/host/session_manager_factory.h"
25 #include "remoting/protocol/it2me_host_authenticator_factory.h"
26 #include "remoting/protocol/network_settings.h"
27 #include "remoting/signaling/server_log_entry.h"
29 namespace remoting {
31 namespace {
33 // This is used for tagging system event logs.
34 const char kApplicationName[] = "chromoting";
35 const int kMaxLoginAttempts = 5;
37 } // namespace
39 It2MeHost::It2MeHost(
40 scoped_ptr<ChromotingHostContext> host_context,
41 scoped_ptr<PolicyWatcher> policy_watcher,
42 scoped_ptr<It2MeConfirmationDialogFactory> confirmation_dialog_factory,
43 base::WeakPtr<It2MeHost::Observer> observer,
44 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
45 const std::string& directory_bot_jid)
46 : host_context_(host_context.Pass()),
47 task_runner_(host_context_->ui_task_runner()),
48 observer_(observer),
49 xmpp_server_config_(xmpp_server_config),
50 directory_bot_jid_(directory_bot_jid),
51 state_(kDisconnected),
52 failed_login_attempts_(0),
53 policy_watcher_(policy_watcher.Pass()),
54 confirmation_dialog_factory_(confirmation_dialog_factory.Pass()),
55 nat_traversal_enabled_(false),
56 policy_received_(false) {
57 DCHECK(task_runner_->BelongsToCurrentThread());
60 void It2MeHost::Connect() {
61 if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
62 DCHECK(task_runner_->BelongsToCurrentThread());
63 host_context_->ui_task_runner()->PostTask(
64 FROM_HERE, base::Bind(&It2MeHost::Connect, this));
65 return;
68 desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
69 host_context_->network_task_runner(),
70 host_context_->input_task_runner(),
71 host_context_->ui_task_runner()));
73 // Start monitoring configured policies.
74 policy_watcher_->StartWatching(
75 base::Bind(&It2MeHost::OnPolicyUpdate, this),
76 base::Bind(&It2MeHost::OnPolicyError, this));
78 // Switch to the network thread to start the actual connection.
79 host_context_->network_task_runner()->PostTask(
80 FROM_HERE, base::Bind(&It2MeHost::ShowConfirmationPrompt, this));
83 void It2MeHost::Disconnect() {
84 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
85 DCHECK(task_runner_->BelongsToCurrentThread());
86 host_context_->network_task_runner()->PostTask(
87 FROM_HERE, base::Bind(&It2MeHost::Disconnect, this));
88 return;
91 switch (state_) {
92 case kDisconnected:
93 ShutdownOnNetworkThread();
94 return;
96 case kStarting:
97 SetState(kDisconnecting);
98 SetState(kDisconnected);
99 ShutdownOnNetworkThread();
100 return;
102 case kDisconnecting:
103 return;
105 default:
106 SetState(kDisconnecting);
108 if (!host_) {
109 SetState(kDisconnected);
110 ShutdownOnNetworkThread();
111 return;
114 // Deleting the host destroys SignalStrategy synchronously, but
115 // SignalStrategy::Listener handlers are not allowed to destroy
116 // SignalStrategy, so post task to destroy the host later.
117 host_context_->network_task_runner()->PostTask(
118 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnNetworkThread, this));
119 return;
123 void It2MeHost::RequestNatPolicy() {
124 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
125 DCHECK(task_runner_->BelongsToCurrentThread());
126 host_context_->network_task_runner()->PostTask(
127 FROM_HERE, base::Bind(&It2MeHost::RequestNatPolicy, this));
128 return;
131 if (policy_received_)
132 UpdateNatPolicy(nat_traversal_enabled_);
135 void It2MeHost::ShowConfirmationPrompt() {
136 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
138 SetState(kStarting);
140 scoped_ptr<It2MeConfirmationDialog> confirmation_dialog =
141 confirmation_dialog_factory_->Create();
143 // TODO(dcaiafa): Remove after dialog implementations for all platforms exist.
144 if (!confirmation_dialog) {
145 ReadPolicyAndConnect();
146 return;
149 confirmation_dialog_proxy_.reset(
150 new It2MeConfirmationDialogProxy(host_context_->ui_task_runner(),
151 confirmation_dialog.Pass()));
153 confirmation_dialog_proxy_->Show(
154 base::Bind(&It2MeHost::OnConfirmationResult, base::Unretained(this)));
157 void It2MeHost::OnConfirmationResult(It2MeConfirmationDialog::Result result) {
158 switch (result) {
159 case It2MeConfirmationDialog::Result::OK:
160 ReadPolicyAndConnect();
161 break;
163 case It2MeConfirmationDialog::Result::CANCEL:
164 Disconnect();
165 break;
167 default:
168 NOTREACHED();
169 return;
173 void It2MeHost::ReadPolicyAndConnect() {
174 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
175 DCHECK_EQ(kStarting, state_);
177 // Only proceed to FinishConnect() if at least one policy update has been
178 // received.
179 if (policy_received_) {
180 FinishConnect();
181 } else {
182 // Otherwise, create the policy watcher, and thunk the connect.
183 pending_connect_ =
184 base::Bind(&It2MeHost::FinishConnect, this);
188 void It2MeHost::FinishConnect() {
189 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
191 if (state_ != kStarting) {
192 // Host has been stopped while we were fetching policy.
193 return;
196 // Check the host domain policy.
197 if (!required_host_domain_.empty() &&
198 !EndsWith(xmpp_server_config_.username,
199 std::string("@") + required_host_domain_, false)) {
200 SetState(kInvalidDomainError);
201 return;
204 // Generate a key pair for the Host to use.
205 // TODO(wez): Move this to the worker thread.
206 host_key_pair_ = RsaKeyPair::Generate();
208 // Create XMPP connection.
209 scoped_ptr<SignalStrategy> signal_strategy(
210 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
211 host_context_->url_request_context_getter(),
212 xmpp_server_config_));
214 // Request registration of the host for support.
215 scoped_ptr<RegisterSupportHostRequest> register_request(
216 new RegisterSupportHostRequest(
217 signal_strategy.get(), host_key_pair_, directory_bot_jid_,
218 base::Bind(&It2MeHost::OnReceivedSupportID,
219 base::Unretained(this))));
221 // Beyond this point nothing can fail, so save the config and request.
222 signal_strategy_ = signal_strategy.Pass();
223 register_request_ = register_request.Pass();
225 // If NAT traversal is off then limit port range to allow firewall pin-holing.
226 HOST_LOG << "NAT state: " << nat_traversal_enabled_;
227 protocol::NetworkSettings network_settings(
228 nat_traversal_enabled_ ?
229 protocol::NetworkSettings::NAT_TRAVERSAL_FULL :
230 protocol::NetworkSettings::NAT_TRAVERSAL_DISABLED);
231 if (!nat_traversal_enabled_) {
232 network_settings.port_range.min_port =
233 protocol::NetworkSettings::kDefaultMinPort;
234 network_settings.port_range.max_port =
235 protocol::NetworkSettings::kDefaultMaxPort;
238 // Create the host.
239 host_.reset(new ChromotingHost(
240 signal_strategy_.get(),
241 desktop_environment_factory_.get(),
242 CreateHostSessionManager(signal_strategy_.get(), network_settings,
243 host_context_->url_request_context_getter()),
244 host_context_->audio_task_runner(),
245 host_context_->input_task_runner(),
246 host_context_->video_capture_task_runner(),
247 host_context_->video_encode_task_runner(),
248 host_context_->network_task_runner(),
249 host_context_->ui_task_runner()));
250 host_->AddStatusObserver(this);
251 host_status_logger_.reset(
252 new HostStatusLogger(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
253 signal_strategy_.get(), directory_bot_jid_));
255 // Disable audio by default.
256 // TODO(sergeyu): Add UI to enable it.
257 scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
258 protocol::CandidateSessionConfig::CreateDefault();
259 protocol_config->DisableAudioChannel();
261 host_->set_protocol_config(protocol_config.Pass());
263 // Create event logger.
264 host_event_logger_ =
265 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
267 // Connect signaling and start the host.
268 signal_strategy_->Connect();
269 host_->Start(xmpp_server_config_.username);
271 SetState(kRequestedAccessCode);
272 return;
275 void It2MeHost::ShutdownOnNetworkThread() {
276 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
277 DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
279 confirmation_dialog_proxy_.reset();
281 if (state_ == kDisconnecting) {
282 host_event_logger_.reset();
283 host_->RemoveStatusObserver(this);
284 host_.reset();
286 register_request_.reset();
287 host_status_logger_.reset();
288 signal_strategy_.reset();
289 SetState(kDisconnected);
292 host_context_->ui_task_runner()->PostTask(
293 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
296 void It2MeHost::ShutdownOnUiThread() {
297 DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
299 // Destroy the DesktopEnvironmentFactory, to free thread references.
300 desktop_environment_factory_.reset();
302 // Stop listening for policy updates.
303 policy_watcher_.reset();
306 void It2MeHost::OnAccessDenied(const std::string& jid) {
307 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
309 ++failed_login_attempts_;
310 if (failed_login_attempts_ == kMaxLoginAttempts) {
311 Disconnect();
315 void It2MeHost::OnClientAuthenticated(const std::string& jid) {
316 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
318 if (state_ == kDisconnecting) {
319 // Ignore the new connection if we are disconnecting.
320 return;
322 if (state_ == kConnected) {
323 // If we already connected another client then one of the connections may be
324 // an attacker, so both are suspect and we have to reject the second
325 // connection and shutdown the host.
326 host_->RejectAuthenticatingClient();
327 Disconnect();
328 return;
331 std::string client_username = jid;
332 size_t pos = client_username.find('/');
333 if (pos != std::string::npos)
334 client_username.replace(pos, std::string::npos, "");
336 HOST_LOG << "Client " << client_username << " connected.";
338 // Pass the client user name to the script object before changing state.
339 task_runner_->PostTask(
340 FROM_HERE, base::Bind(&It2MeHost::Observer::OnClientAuthenticated,
341 observer_, client_username));
343 SetState(kConnected);
346 void It2MeHost::OnClientDisconnected(const std::string& jid) {
347 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
349 Disconnect();
352 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
353 // The policy watcher runs on the |ui_task_runner|.
354 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
355 host_context_->network_task_runner()->PostTask(
356 FROM_HERE,
357 base::Bind(&It2MeHost::OnPolicyUpdate, this, base::Passed(&policies)));
358 return;
361 bool nat_policy;
362 if (policies->GetBoolean(policy::key::kRemoteAccessHostFirewallTraversal,
363 &nat_policy)) {
364 UpdateNatPolicy(nat_policy);
366 std::string host_domain;
367 if (policies->GetString(policy::key::kRemoteAccessHostDomain, &host_domain)) {
368 UpdateHostDomainPolicy(host_domain);
371 policy_received_ = true;
373 if (!pending_connect_.is_null()) {
374 pending_connect_.Run();
375 pending_connect_.Reset();
379 void It2MeHost::OnPolicyError() {
380 // TODO(lukasza): Report the policy error to the user. crbug.com/433009
381 NOTIMPLEMENTED();
384 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
385 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
387 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
389 // When transitioning from enabled to disabled, force disconnect any
390 // existing session.
391 if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
392 Disconnect();
395 nat_traversal_enabled_ = nat_traversal_enabled;
397 // Notify the web-app of the policy setting.
398 task_runner_->PostTask(
399 FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
400 observer_, nat_traversal_enabled_));
403 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
404 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
406 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
408 // When setting a host domain policy, force disconnect any existing session.
409 if (!host_domain.empty() && IsConnected()) {
410 Disconnect();
413 required_host_domain_ = host_domain;
416 It2MeHost::~It2MeHost() {
417 // Check that resources that need to be torn down on the UI thread are gone.
418 DCHECK(!desktop_environment_factory_.get());
419 DCHECK(!policy_watcher_.get());
422 void It2MeHost::SetState(It2MeHostState state) {
423 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
425 switch (state_) {
426 case kDisconnected:
427 DCHECK(state == kStarting ||
428 state == kError) << state;
429 break;
430 case kStarting:
431 DCHECK(state == kRequestedAccessCode ||
432 state == kDisconnecting ||
433 state == kError ||
434 state == kInvalidDomainError) << state;
435 break;
436 case kRequestedAccessCode:
437 DCHECK(state == kReceivedAccessCode ||
438 state == kDisconnecting ||
439 state == kError) << state;
440 break;
441 case kReceivedAccessCode:
442 DCHECK(state == kConnected ||
443 state == kDisconnecting ||
444 state == kError) << state;
445 break;
446 case kConnected:
447 DCHECK(state == kDisconnecting ||
448 state == kDisconnected ||
449 state == kError) << state;
450 break;
451 case kDisconnecting:
452 DCHECK(state == kDisconnected) << state;
453 break;
454 case kError:
455 DCHECK(state == kDisconnecting) << state;
456 break;
457 case kInvalidDomainError:
458 DCHECK(state == kDisconnecting) << state;
459 break;
462 state_ = state;
464 // Post a state-change notification to the web-app.
465 task_runner_->PostTask(
466 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
467 observer_, state));
470 bool It2MeHost::IsConnected() const {
471 return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
472 state_ == kConnected;
475 void It2MeHost::OnReceivedSupportID(
476 bool success,
477 const std::string& support_id,
478 const base::TimeDelta& lifetime) {
479 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
481 if (!success) {
482 SetState(kError);
483 Disconnect();
484 return;
487 std::string host_secret = GenerateSupportHostSecret();
488 std::string access_code = support_id + host_secret;
490 std::string local_certificate = host_key_pair_->GenerateCertificate();
491 if (local_certificate.empty()) {
492 LOG(ERROR) << "Failed to generate host certificate.";
493 SetState(kError);
494 Disconnect();
495 return;
498 scoped_ptr<protocol::AuthenticatorFactory> factory(
499 new protocol::It2MeHostAuthenticatorFactory(
500 local_certificate, host_key_pair_, access_code));
501 host_->SetAuthenticatorFactory(factory.Pass());
503 // Pass the Access Code to the script object before changing state.
504 task_runner_->PostTask(
505 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
506 observer_, access_code, lifetime));
508 SetState(kReceivedAccessCode);
511 It2MeHostFactory::It2MeHostFactory() : policy_service_(nullptr) {
514 It2MeHostFactory::~It2MeHostFactory() {}
516 void It2MeHostFactory::set_policy_service(
517 policy::PolicyService* policy_service) {
518 DCHECK(policy_service);
519 DCHECK(!policy_service_) << "|policy_service| can only be set once.";
520 policy_service_ = policy_service;
523 scoped_refptr<It2MeHost> It2MeHostFactory::CreateIt2MeHost(
524 scoped_ptr<ChromotingHostContext> context,
525 base::WeakPtr<It2MeHost::Observer> observer,
526 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
527 const std::string& directory_bot_jid) {
528 DCHECK(context->ui_task_runner()->BelongsToCurrentThread());
530 scoped_ptr<It2MeConfirmationDialogFactory> confirmation_dialog_factory(
531 new It2MeConfirmationDialogFactory());
532 scoped_ptr<PolicyWatcher> policy_watcher =
533 PolicyWatcher::Create(policy_service_, context->file_task_runner());
534 return new It2MeHost(context.Pass(), policy_watcher.Pass(),
535 confirmation_dialog_factory.Pass(),
536 observer, xmpp_server_config, directory_bot_jid);
539 } // namespace remoting