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