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/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/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/jingle_glue/network_settings.h"
24 #include "remoting/protocol/it2me_host_authenticator_factory.h"
25
26 namespace remoting {
27
28 namespace {
29
30 // This is used for tagging system event logs.
31 const char kApplicationName[] = "chromoting";
32 const int kMaxLoginAttempts = 5;
33
34 } // namespace
35
It2MeHost(ChromotingHostContext * host_context,scoped_refptr<base::SingleThreadTaskRunner> task_runner,base::WeakPtr<It2MeHost::Observer> observer,const XmppSignalStrategy::XmppServerConfig & xmpp_server_config,const std::string & directory_bot_jid)36 It2MeHost::It2MeHost(
37 ChromotingHostContext* host_context,
38 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
39 base::WeakPtr<It2MeHost::Observer> observer,
40 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
41 const std::string& directory_bot_jid)
42 : host_context_(host_context),
43 task_runner_(task_runner),
44 observer_(observer),
45 xmpp_server_config_(xmpp_server_config),
46 directory_bot_jid_(directory_bot_jid),
47 state_(kDisconnected),
48 failed_login_attempts_(0),
49 nat_traversal_enabled_(false),
50 policy_received_(false) {
51 DCHECK(task_runner_->BelongsToCurrentThread());
52 }
53
Connect()54 void It2MeHost::Connect() {
55 if (!host_context_->ui_task_runner()->BelongsToCurrentThread()) {
56 DCHECK(task_runner_->BelongsToCurrentThread());
57 host_context_->ui_task_runner()->PostTask(
58 FROM_HERE, base::Bind(&It2MeHost::Connect, this));
59 return;
60 }
61
62 desktop_environment_factory_.reset(new It2MeDesktopEnvironmentFactory(
63 host_context_->network_task_runner(),
64 host_context_->input_task_runner(),
65 host_context_->ui_task_runner()));
66
67 // Start monitoring configured policies.
68 policy_watcher_.reset(
69 policy_hack::PolicyWatcher::Create(host_context_->network_task_runner()));
70 policy_watcher_->StartWatching(
71 base::Bind(&It2MeHost::OnPolicyUpdate, this));
72
73 // Switch to the network thread to start the actual connection.
74 host_context_->network_task_runner()->PostTask(
75 FROM_HERE, base::Bind(&It2MeHost::ReadPolicyAndConnect, this));
76 }
77
Disconnect()78 void It2MeHost::Disconnect() {
79 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
80 DCHECK(task_runner_->BelongsToCurrentThread());
81 host_context_->network_task_runner()->PostTask(
82 FROM_HERE, base::Bind(&It2MeHost::Disconnect, this));
83 return;
84 }
85
86 switch (state_) {
87 case kDisconnected:
88 ShutdownOnNetworkThread();
89 return;
90
91 case kStarting:
92 SetState(kDisconnecting);
93 SetState(kDisconnected);
94 ShutdownOnNetworkThread();
95 return;
96
97 case kDisconnecting:
98 return;
99
100 default:
101 SetState(kDisconnecting);
102
103 if (!host_) {
104 SetState(kDisconnected);
105 ShutdownOnNetworkThread();
106 return;
107 }
108
109 // Deleting the host destroys SignalStrategy synchronously, but
110 // SignalStrategy::Listener handlers are not allowed to destroy
111 // SignalStrategy, so post task to destroy the host later.
112 host_context_->network_task_runner()->PostTask(
113 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnNetworkThread, this));
114 return;
115 }
116 }
117
RequestNatPolicy()118 void It2MeHost::RequestNatPolicy() {
119 if (!host_context_->network_task_runner()->BelongsToCurrentThread()) {
120 DCHECK(task_runner_->BelongsToCurrentThread());
121 host_context_->network_task_runner()->PostTask(
122 FROM_HERE, base::Bind(&It2MeHost::RequestNatPolicy, this));
123 return;
124 }
125
126 if (policy_received_)
127 UpdateNatPolicy(nat_traversal_enabled_);
128 }
129
ReadPolicyAndConnect()130 void It2MeHost::ReadPolicyAndConnect() {
131 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
132
133 SetState(kStarting);
134
135 // Only proceed to FinishConnect() if at least one policy update has been
136 // received.
137 if (policy_received_) {
138 FinishConnect();
139 } else {
140 // Otherwise, create the policy watcher, and thunk the connect.
141 pending_connect_ =
142 base::Bind(&It2MeHost::FinishConnect, this);
143 }
144 }
145
FinishConnect()146 void It2MeHost::FinishConnect() {
147 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
148
149 if (state_ != kStarting) {
150 // Host has been stopped while we were fetching policy.
151 return;
152 }
153
154 // Check the host domain policy.
155 if (!required_host_domain_.empty() &&
156 !EndsWith(xmpp_server_config_.username,
157 std::string("@") + required_host_domain_, false)) {
158 SetState(kInvalidDomainError);
159 return;
160 }
161
162 // Generate a key pair for the Host to use.
163 // TODO(wez): Move this to the worker thread.
164 host_key_pair_ = RsaKeyPair::Generate();
165
166 // Create XMPP connection.
167 scoped_ptr<SignalStrategy> signal_strategy(
168 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
169 host_context_->url_request_context_getter(),
170 xmpp_server_config_));
171
172 // Request registration of the host for support.
173 scoped_ptr<RegisterSupportHostRequest> register_request(
174 new RegisterSupportHostRequest(
175 signal_strategy.get(), host_key_pair_, directory_bot_jid_,
176 base::Bind(&It2MeHost::OnReceivedSupportID,
177 base::Unretained(this))));
178
179 // Beyond this point nothing can fail, so save the config and request.
180 signal_strategy_ = signal_strategy.Pass();
181 register_request_ = register_request.Pass();
182
183 // If NAT traversal is off then limit port range to allow firewall pin-holing.
184 HOST_LOG << "NAT state: " << nat_traversal_enabled_;
185 NetworkSettings network_settings(
186 nat_traversal_enabled_ ?
187 NetworkSettings::NAT_TRAVERSAL_ENABLED :
188 NetworkSettings::NAT_TRAVERSAL_DISABLED);
189 if (!nat_traversal_enabled_) {
190 network_settings.min_port = NetworkSettings::kDefaultMinPort;
191 network_settings.max_port = NetworkSettings::kDefaultMaxPort;
192 }
193
194 // Create the host.
195 host_.reset(new ChromotingHost(
196 signal_strategy_.get(),
197 desktop_environment_factory_.get(),
198 CreateHostSessionManager(signal_strategy_.get(), network_settings,
199 host_context_->url_request_context_getter()),
200 host_context_->audio_task_runner(),
201 host_context_->input_task_runner(),
202 host_context_->video_capture_task_runner(),
203 host_context_->video_encode_task_runner(),
204 host_context_->network_task_runner(),
205 host_context_->ui_task_runner()));
206 host_->AddStatusObserver(this);
207 log_to_server_.reset(
208 new LogToServer(host_->AsWeakPtr(), ServerLogEntry::IT2ME,
209 signal_strategy_.get(), directory_bot_jid_));
210
211 // Disable audio by default.
212 // TODO(sergeyu): Add UI to enable it.
213 scoped_ptr<protocol::CandidateSessionConfig> protocol_config =
214 protocol::CandidateSessionConfig::CreateDefault();
215 protocol::CandidateSessionConfig::DisableAudioChannel(protocol_config.get());
216
217 // VP9 encode is not yet supported.
218 protocol::CandidateSessionConfig::DisableVideoCodec(
219 protocol_config.get(), protocol::ChannelConfig::CODEC_VP9);
220
221 host_->set_protocol_config(protocol_config.Pass());
222
223 // Create event logger.
224 host_event_logger_ =
225 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
226
227 // Connect signaling and start the host.
228 signal_strategy_->Connect();
229 host_->Start(xmpp_server_config_.username);
230
231 SetState(kRequestedAccessCode);
232 return;
233 }
234
ShutdownOnNetworkThread()235 void It2MeHost::ShutdownOnNetworkThread() {
236 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
237 DCHECK(state_ == kDisconnecting || state_ == kDisconnected);
238
239 if (state_ == kDisconnecting) {
240 host_event_logger_.reset();
241 host_->RemoveStatusObserver(this);
242 host_.reset();
243
244 register_request_.reset();
245 log_to_server_.reset();
246 signal_strategy_.reset();
247 SetState(kDisconnected);
248 }
249
250 host_context_->ui_task_runner()->PostTask(
251 FROM_HERE, base::Bind(&It2MeHost::ShutdownOnUiThread, this));
252 }
253
ShutdownOnUiThread()254 void It2MeHost::ShutdownOnUiThread() {
255 DCHECK(host_context_->ui_task_runner()->BelongsToCurrentThread());
256
257 // Destroy the DesktopEnvironmentFactory, to free thread references.
258 desktop_environment_factory_.reset();
259
260 // Stop listening for policy updates.
261 if (policy_watcher_.get()) {
262 base::WaitableEvent policy_watcher_stopped_(true, false);
263 policy_watcher_->StopWatching(&policy_watcher_stopped_);
264 policy_watcher_stopped_.Wait();
265 policy_watcher_.reset();
266 }
267 }
268
OnAccessDenied(const std::string & jid)269 void It2MeHost::OnAccessDenied(const std::string& jid) {
270 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
271
272 ++failed_login_attempts_;
273 if (failed_login_attempts_ == kMaxLoginAttempts) {
274 Disconnect();
275 }
276 }
277
OnClientAuthenticated(const std::string & jid)278 void It2MeHost::OnClientAuthenticated(const std::string& jid) {
279 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
280
281 if (state_ == kDisconnecting) {
282 // Ignore the new connection if we are disconnecting.
283 return;
284 }
285 if (state_ == kConnected) {
286 // If we already connected another client then one of the connections may be
287 // an attacker, so both are suspect and we have to reject the second
288 // connection and shutdown the host.
289 host_->RejectAuthenticatingClient();
290 Disconnect();
291 return;
292 }
293
294 std::string client_username = jid;
295 size_t pos = client_username.find('/');
296 if (pos != std::string::npos)
297 client_username.replace(pos, std::string::npos, "");
298
299 HOST_LOG << "Client " << client_username << " connected.";
300
301 // Pass the client user name to the script object before changing state.
302 task_runner_->PostTask(
303 FROM_HERE, base::Bind(&It2MeHost::Observer::OnClientAuthenticated,
304 observer_, client_username));
305
306 SetState(kConnected);
307 }
308
OnClientDisconnected(const std::string & jid)309 void It2MeHost::OnClientDisconnected(const std::string& jid) {
310 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
311
312 Disconnect();
313 }
314
OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies)315 void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
316 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
317
318 bool nat_policy;
319 if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
320 &nat_policy)) {
321 UpdateNatPolicy(nat_policy);
322 }
323 std::string host_domain;
324 if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
325 &host_domain)) {
326 UpdateHostDomainPolicy(host_domain);
327 }
328
329 policy_received_ = true;
330
331 if (!pending_connect_.is_null()) {
332 pending_connect_.Run();
333 pending_connect_.Reset();
334 }
335 }
336
UpdateNatPolicy(bool nat_traversal_enabled)337 void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
338 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
339
340 VLOG(2) << "UpdateNatPolicy: " << nat_traversal_enabled;
341
342 // When transitioning from enabled to disabled, force disconnect any
343 // existing session.
344 if (nat_traversal_enabled_ && !nat_traversal_enabled && IsConnected()) {
345 Disconnect();
346 }
347
348 nat_traversal_enabled_ = nat_traversal_enabled;
349
350 // Notify the web-app of the policy setting.
351 task_runner_->PostTask(
352 FROM_HERE, base::Bind(&It2MeHost::Observer::OnNatPolicyChanged,
353 observer_, nat_traversal_enabled_));
354 }
355
UpdateHostDomainPolicy(const std::string & host_domain)356 void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
357 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
358
359 VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
360
361 // When setting a host domain policy, force disconnect any existing session.
362 if (!host_domain.empty() && IsConnected()) {
363 Disconnect();
364 }
365
366 required_host_domain_ = host_domain;
367 }
368
~It2MeHost()369 It2MeHost::~It2MeHost() {
370 // Check that resources that need to be torn down on the UI thread are gone.
371 DCHECK(!desktop_environment_factory_.get());
372 DCHECK(!policy_watcher_.get());
373 }
374
SetState(It2MeHostState state)375 void It2MeHost::SetState(It2MeHostState state) {
376 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
377
378 switch (state_) {
379 case kDisconnected:
380 DCHECK(state == kStarting ||
381 state == kError) << state;
382 break;
383 case kStarting:
384 DCHECK(state == kRequestedAccessCode ||
385 state == kDisconnecting ||
386 state == kError ||
387 state == kInvalidDomainError) << state;
388 break;
389 case kRequestedAccessCode:
390 DCHECK(state == kReceivedAccessCode ||
391 state == kDisconnecting ||
392 state == kError) << state;
393 break;
394 case kReceivedAccessCode:
395 DCHECK(state == kConnected ||
396 state == kDisconnecting ||
397 state == kError) << state;
398 break;
399 case kConnected:
400 DCHECK(state == kDisconnecting ||
401 state == kDisconnected ||
402 state == kError) << state;
403 break;
404 case kDisconnecting:
405 DCHECK(state == kDisconnected) << state;
406 break;
407 case kError:
408 DCHECK(state == kDisconnecting) << state;
409 break;
410 case kInvalidDomainError:
411 DCHECK(state == kDisconnecting) << state;
412 break;
413 };
414
415 state_ = state;
416
417 // Post a state-change notification to the web-app.
418 task_runner_->PostTask(
419 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStateChanged,
420 observer_, state));
421 }
422
IsConnected() const423 bool It2MeHost::IsConnected() const {
424 return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode ||
425 state_ == kConnected;
426 }
427
OnReceivedSupportID(bool success,const std::string & support_id,const base::TimeDelta & lifetime)428 void It2MeHost::OnReceivedSupportID(
429 bool success,
430 const std::string& support_id,
431 const base::TimeDelta& lifetime) {
432 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
433
434 if (!success) {
435 SetState(kError);
436 Disconnect();
437 return;
438 }
439
440 std::string host_secret = GenerateSupportHostSecret();
441 std::string access_code = support_id + host_secret;
442
443 std::string local_certificate = host_key_pair_->GenerateCertificate();
444 if (local_certificate.empty()) {
445 LOG(ERROR) << "Failed to generate host certificate.";
446 SetState(kError);
447 Disconnect();
448 return;
449 }
450
451 scoped_ptr<protocol::AuthenticatorFactory> factory(
452 new protocol::It2MeHostAuthenticatorFactory(
453 local_certificate, host_key_pair_, access_code));
454 host_->SetAuthenticatorFactory(factory.Pass());
455
456 // Pass the Access Code to the script object before changing state.
457 task_runner_->PostTask(
458 FROM_HERE, base::Bind(&It2MeHost::Observer::OnStoreAccessCode,
459 observer_, access_code, lifetime));
460
461 SetState(kReceivedAccessCode);
462 }
463
It2MeHostFactory()464 It2MeHostFactory::It2MeHostFactory() {}
465
~It2MeHostFactory()466 It2MeHostFactory::~It2MeHostFactory() {}
467
CreateIt2MeHost(ChromotingHostContext * context,scoped_refptr<base::SingleThreadTaskRunner> task_runner,base::WeakPtr<It2MeHost::Observer> observer,const XmppSignalStrategy::XmppServerConfig & xmpp_server_config,const std::string & directory_bot_jid)468 scoped_refptr<It2MeHost> It2MeHostFactory::CreateIt2MeHost(
469 ChromotingHostContext* context,
470 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
471 base::WeakPtr<It2MeHost::Observer> observer,
472 const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
473 const std::string& directory_bot_jid) {
474 return new It2MeHost(
475 context, task_runner, observer, xmpp_server_config, directory_bot_jid);
476 }
477
478 } // namespace remoting
479