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