• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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