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"
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"
34 // This is used for tagging system event logs.
35 const char kApplicationName
[] = "chromoting";
36 const int kMaxLoginAttempts
= 5;
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()),
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));
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));
94 ShutdownOnNetworkThread();
98 SetState(kDisconnecting
, "");
99 SetState(kDisconnected
, "");
100 ShutdownOnNetworkThread();
107 SetState(kDisconnecting
, "");
110 SetState(kDisconnected
, "");
111 ShutdownOnNetworkThread();
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));
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));
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();
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
) {
160 case It2MeConfirmationDialog::Result::OK
:
161 ReadPolicyAndConnect();
164 case It2MeConfirmationDialog::Result::CANCEL
:
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
180 if (policy_received_
) {
183 // Otherwise, create the policy watcher, and thunk the 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.
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
, "");
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 scoped_ptr
<protocol::SessionManager
> session_manager
=
241 CreateHostSessionManager(signal_strategy_
.get(), network_settings
,
242 host_context_
->url_request_context_getter());
243 scoped_ptr
<protocol::CandidateSessionConfig
> protocol_config
=
244 protocol::CandidateSessionConfig::CreateDefault();
245 // Disable audio by default.
246 // TODO(sergeyu): Add UI to enable it.
247 protocol_config
->DisableAudioChannel();
248 session_manager
->set_protocol_config(protocol_config
.Pass());
251 host_
.reset(new ChromotingHost(
252 signal_strategy_
.get(), desktop_environment_factory_
.get(),
253 session_manager
.Pass(), host_context_
->audio_task_runner(),
254 host_context_
->input_task_runner(),
255 host_context_
->video_capture_task_runner(),
256 host_context_
->video_encode_task_runner(),
257 host_context_
->network_task_runner(), host_context_
->ui_task_runner()));
258 host_
->AddStatusObserver(this);
259 host_status_logger_
.reset(
260 new HostStatusLogger(host_
->AsWeakPtr(), ServerLogEntry::IT2ME
,
261 signal_strategy_
.get(), directory_bot_jid_
));
263 // Create 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
, "");
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);
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
) {
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.
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();
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());
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(
357 base::Bind(&It2MeHost::OnPolicyUpdate
, this, base::Passed(&policies
)));
362 if (policies
->GetBoolean(policy::key::kRemoteAccessHostFirewallTraversal
,
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 base::ResetAndReturn(&pending_connect_
).Run();
378 void It2MeHost::OnPolicyError() {
379 // TODO(lukasza): Report the policy error to the user. crbug.com/433009
383 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled
) {
384 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
386 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled
;
388 // When transitioning from enabled to disabled, force disconnect any
390 if (nat_traversal_enabled_
&& !nat_traversal_enabled
&& IsConnected()) {
394 nat_traversal_enabled_
= nat_traversal_enabled
;
396 // Notify the web-app of the policy setting.
397 task_runner_
->PostTask(
398 FROM_HERE
, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged
,
399 observer_
, nat_traversal_enabled_
));
402 void It2MeHost::UpdateHostDomainPolicy(const std::string
& host_domain
) {
403 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
405 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain
;
407 // When setting a host domain policy, force disconnect any existing session.
408 if (!host_domain
.empty() && IsConnected()) {
412 required_host_domain_
= host_domain
;
415 It2MeHost::~It2MeHost() {
416 // Check that resources that need to be torn down on the UI thread are gone.
417 DCHECK(!desktop_environment_factory_
.get());
418 DCHECK(!policy_watcher_
.get());
421 void It2MeHost::SetState(It2MeHostState state
,
422 const std::string
& error_message
) {
423 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
427 DCHECK(state
== kStarting
||
428 state
== kError
) << state
;
431 DCHECK(state
== kRequestedAccessCode
||
432 state
== kDisconnecting
||
434 state
== kInvalidDomainError
) << state
;
436 case kRequestedAccessCode
:
437 DCHECK(state
== kReceivedAccessCode
||
438 state
== kDisconnecting
||
439 state
== kError
) << state
;
441 case kReceivedAccessCode
:
442 DCHECK(state
== kConnected
||
443 state
== kDisconnecting
||
444 state
== kError
) << state
;
447 DCHECK(state
== kDisconnecting
||
448 state
== kDisconnected
||
449 state
== kError
) << state
;
452 DCHECK(state
== kDisconnected
) << state
;
455 DCHECK(state
== kDisconnecting
) << state
;
457 case kInvalidDomainError
:
458 DCHECK(state
== kDisconnecting
) << 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
, error_message
));
470 bool It2MeHost::IsConnected() const {
471 return state_
== kRequestedAccessCode
|| state_
== kReceivedAccessCode
||
472 state_
== kConnected
;
475 void It2MeHost::OnReceivedSupportID(
476 const std::string
& support_id
,
477 const base::TimeDelta
& lifetime
,
478 const std::string
& error_message
) {
479 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
481 if (!error_message
.empty()) {
482 SetState(kError
, error_message
);
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 std::string error_message
= "Failed to generate host certificate.";
493 LOG(ERROR
) << error_message
;
494 SetState(kError
, error_message
);
499 scoped_ptr
<protocol::AuthenticatorFactory
> factory(
500 new protocol::It2MeHostAuthenticatorFactory(
501 local_certificate
, host_key_pair_
, access_code
));
502 host_
->SetAuthenticatorFactory(factory
.Pass());
504 // Pass the Access Code to the script object before changing state.
505 task_runner_
->PostTask(
506 FROM_HERE
, base::Bind(&It2MeHost::Observer::OnStoreAccessCode
,
507 observer_
, access_code
, lifetime
));
509 SetState(kReceivedAccessCode
, "");
512 It2MeHostFactory::It2MeHostFactory() : policy_service_(nullptr) {
515 It2MeHostFactory::~It2MeHostFactory() {}
517 void It2MeHostFactory::set_policy_service(
518 policy::PolicyService
* policy_service
) {
519 DCHECK(policy_service
);
520 DCHECK(!policy_service_
) << "|policy_service| can only be set once.";
521 policy_service_
= policy_service
;
524 scoped_refptr
<It2MeHost
> It2MeHostFactory::CreateIt2MeHost(
525 scoped_ptr
<ChromotingHostContext
> context
,
526 base::WeakPtr
<It2MeHost::Observer
> observer
,
527 const XmppSignalStrategy::XmppServerConfig
& xmpp_server_config
,
528 const std::string
& directory_bot_jid
) {
529 DCHECK(context
->ui_task_runner()->BelongsToCurrentThread());
531 scoped_ptr
<It2MeConfirmationDialogFactory
> confirmation_dialog_factory(
532 new It2MeConfirmationDialogFactory());
533 scoped_ptr
<PolicyWatcher
> policy_watcher
=
534 PolicyWatcher::Create(policy_service_
, context
->file_task_runner());
535 return new It2MeHost(context
.Pass(), policy_watcher
.Pass(),
536 confirmation_dialog_factory
.Pass(),
537 observer
, xmpp_server_config
, directory_bot_jid
);
540 } // namespace remoting