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/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"
31 // This is used for tagging system event logs.
32 const char kApplicationName
[] = "chromoting";
33 const int kMaxLoginAttempts
= 5;
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()),
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());
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));
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()));
69 // Start monitoring configured policies.
70 policy_watcher_
->StartWatching(base::Bind(&It2MeHost::OnPolicyUpdate
, this));
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));
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));
87 ShutdownOnNetworkThread();
91 SetState(kDisconnecting
);
92 SetState(kDisconnected
);
93 ShutdownOnNetworkThread();
100 SetState(kDisconnecting
);
103 SetState(kDisconnected
);
104 ShutdownOnNetworkThread();
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));
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));
125 if (policy_received_
)
126 UpdateNatPolicy(nat_traversal_enabled_
);
129 void It2MeHost::ReadPolicyAndConnect() {
130 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
134 // Only proceed to FinishConnect() if at least one policy update has been
136 if (policy_received_
) {
139 // Otherwise, create the policy watcher, and thunk the connect.
141 base::Bind(&It2MeHost::FinishConnect
, this);
145 void It2MeHost::FinishConnect() {
146 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
148 if (state_
!= kStarting
) {
149 // Host has been stopped while we were fetching policy.
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
);
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();
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_
));
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))));
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();
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
;
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_
));
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();
216 host_
->set_protocol_config(protocol_config
.Pass());
218 // Create event logger.
220 HostEventLogger::Create(host_
->AsWeakPtr(), kApplicationName
);
222 // Connect signaling and start the host.
223 signal_strategy_
->Connect();
224 host_
->Start(xmpp_server_config_
.username
);
226 SetState(kRequestedAccessCode
);
230 void It2MeHost::ShutdownOnNetworkThread() {
231 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
232 DCHECK(state_
== kDisconnecting
|| state_
== kDisconnected
);
234 if (state_
== kDisconnecting
) {
235 host_event_logger_
.reset();
236 host_
->RemoveStatusObserver(this);
239 register_request_
.reset();
240 host_status_logger_
.reset();
241 signal_strategy_
.reset();
242 SetState(kDisconnected
);
245 host_context_
->ui_task_runner()->PostTask(
246 FROM_HERE
, base::Bind(&It2MeHost::ShutdownOnUiThread
, this));
249 void It2MeHost::ShutdownOnUiThread() {
250 DCHECK(host_context_
->ui_task_runner()->BelongsToCurrentThread());
252 // Destroy the DesktopEnvironmentFactory, to free thread references.
253 desktop_environment_factory_
.reset();
255 // Stop listening for policy updates.
256 if (policy_watcher_
.get()) {
257 policy_watcher_
->StopWatching(
258 base::Bind(&It2MeHost::OnPolicyWatcherShutdown
, this));
263 void It2MeHost::OnPolicyWatcherShutdown() {
264 policy_watcher_
.reset();
267 void It2MeHost::OnAccessDenied(const std::string
& jid
) {
268 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
270 ++failed_login_attempts_
;
271 if (failed_login_attempts_
== kMaxLoginAttempts
) {
276 void It2MeHost::OnClientAuthenticated(const std::string
& jid
) {
277 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
279 if (state_
== kDisconnecting
) {
280 // Ignore the new connection if we are disconnecting.
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();
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
, "");
297 HOST_LOG
<< "Client " << client_username
<< " connected.";
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
));
304 SetState(kConnected
);
307 void It2MeHost::OnClientDisconnected(const std::string
& jid
) {
308 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
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(
319 base::Bind(&It2MeHost::OnPolicyUpdate
, this, base::Passed(&policies
)));
324 if (policies
->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName
,
326 UpdateNatPolicy(nat_policy
);
328 std::string host_domain
;
329 if (policies
->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName
,
331 UpdateHostDomainPolicy(host_domain
);
334 policy_received_
= true;
336 if (!pending_connect_
.is_null()) {
337 pending_connect_
.Run();
338 pending_connect_
.Reset();
342 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled
) {
343 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
345 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled
;
347 // When transitioning from enabled to disabled, force disconnect any
349 if (nat_traversal_enabled_
&& !nat_traversal_enabled
&& IsConnected()) {
353 nat_traversal_enabled_
= nat_traversal_enabled
;
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_
));
361 void It2MeHost::UpdateHostDomainPolicy(const std::string
& host_domain
) {
362 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
364 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain
;
366 // When setting a host domain policy, force disconnect any existing session.
367 if (!host_domain
.empty() && IsConnected()) {
371 required_host_domain_
= host_domain
;
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());
380 void It2MeHost::SetState(It2MeHostState state
) {
381 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
385 DCHECK(state
== kStarting
||
386 state
== kError
) << state
;
389 DCHECK(state
== kRequestedAccessCode
||
390 state
== kDisconnecting
||
392 state
== kInvalidDomainError
) << state
;
394 case kRequestedAccessCode
:
395 DCHECK(state
== kReceivedAccessCode
||
396 state
== kDisconnecting
||
397 state
== kError
) << state
;
399 case kReceivedAccessCode
:
400 DCHECK(state
== kConnected
||
401 state
== kDisconnecting
||
402 state
== kError
) << state
;
405 DCHECK(state
== kDisconnecting
||
406 state
== kDisconnected
||
407 state
== kError
) << state
;
410 DCHECK(state
== kDisconnected
) << state
;
413 DCHECK(state
== kDisconnecting
) << state
;
415 case kInvalidDomainError
:
416 DCHECK(state
== kDisconnecting
) << state
;
422 // Post a state-change notification to the web-app.
423 task_runner_
->PostTask(
424 FROM_HERE
, base::Bind(&It2MeHost::Observer::OnStateChanged
,
428 bool It2MeHost::IsConnected() const {
429 return state_
== kRequestedAccessCode
|| state_
== kReceivedAccessCode
||
430 state_
== kConnected
;
433 void It2MeHost::OnReceivedSupportID(
435 const std::string
& support_id
,
436 const base::TimeDelta
& lifetime
) {
437 DCHECK(host_context_
->network_task_runner()->BelongsToCurrentThread());
445 std::string host_secret
= GenerateSupportHostSecret();
446 std::string access_code
= support_id
+ host_secret
;
448 std::string local_certificate
= host_key_pair_
->GenerateCertificate();
449 if (local_certificate
.empty()) {
450 LOG(ERROR
) << "Failed to generate host certificate.";
456 scoped_ptr
<protocol::AuthenticatorFactory
> factory(
457 new protocol::It2MeHostAuthenticatorFactory(
458 local_certificate
, host_key_pair_
, access_code
));
459 host_
->SetAuthenticatorFactory(factory
.Pass());
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
));
466 SetState(kReceivedAccessCode
);
469 It2MeHostFactory::It2MeHostFactory() : policy_service_(nullptr) {
472 It2MeHostFactory::~It2MeHostFactory() {}
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
;
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
);
493 } // namespace remoting