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 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 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 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 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 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 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 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 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 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 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 309 void It2MeHost::OnClientDisconnected(const std::string& jid) { 310 DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread()); 311 312 Disconnect(); 313 } 314 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 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 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 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 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 423 bool It2MeHost::IsConnected() const { 424 return state_ == kRequestedAccessCode || state_ == kReceivedAccessCode || 425 state_ == kConnected; 426 } 427 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 464 It2MeHostFactory::It2MeHostFactory() {} 465 466 It2MeHostFactory::~It2MeHostFactory() {} 467 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