remoting_me2me_host.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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// This file implements a standalone host process for Me2Me. 6 7#include <string> 8 9#include "base/at_exit.h" 10#include "base/bind.h" 11#include "base/callback.h" 12#include "base/command_line.h" 13#include "base/file_path.h" 14#include "base/file_util.h" 15#include "base/memory/scoped_ptr.h" 16#include "base/message_loop.h" 17#include "base/scoped_native_library.h" 18#include "base/string_number_conversions.h" 19#include "base/string_util.h" 20#include "base/stringize_macros.h" 21#include "base/synchronization/waitable_event.h" 22#include "base/threading/thread.h" 23#include "base/utf_string_conversions.h" 24#include "base/win/windows_version.h" 25#include "build/build_config.h" 26#include "crypto/nss_util.h" 27#include "ipc/ipc_channel.h" 28#include "ipc/ipc_channel_proxy.h" 29#include "ipc/ipc_listener.h" 30#include "net/base/network_change_notifier.h" 31#include "net/socket/ssl_server_socket.h" 32#include "remoting/base/auto_thread_task_runner.h" 33#include "remoting/base/breakpad.h" 34#include "remoting/base/constants.h" 35#include "remoting/host/branding.h" 36#include "remoting/host/chromoting_host.h" 37#include "remoting/host/chromoting_host_context.h" 38#include "remoting/host/chromoting_messages.h" 39#include "remoting/host/config_file_watcher.h" 40#include "remoting/host/curtain_mode.h" 41#include "remoting/host/curtaining_host_observer.h" 42#include "remoting/host/desktop_environment_factory.h" 43#include "remoting/host/desktop_resizer.h" 44#include "remoting/host/desktop_session_connector.h" 45#include "remoting/host/dns_blackhole_checker.h" 46#include "remoting/host/event_executor.h" 47#include "remoting/host/heartbeat_sender.h" 48#include "remoting/host/host_config.h" 49#include "remoting/host/host_event_logger.h" 50#include "remoting/host/host_exit_codes.h" 51#include "remoting/host/host_user_interface.h" 52#include "remoting/host/ipc_consts.h" 53#include "remoting/host/ipc_desktop_environment_factory.h" 54#include "remoting/host/json_host_config.h" 55#include "remoting/host/logging.h" 56#include "remoting/host/log_to_server.h" 57#include "remoting/host/network_settings.h" 58#include "remoting/host/policy_hack/policy_watcher.h" 59#include "remoting/host/resizing_host_observer.h" 60#include "remoting/host/session_manager_factory.h" 61#include "remoting/host/signaling_connector.h" 62#include "remoting/host/usage_stats_consent.h" 63#include "remoting/host/video_frame_capturer.h" 64#include "remoting/jingle_glue/xmpp_signal_strategy.h" 65#include "remoting/protocol/me2me_host_authenticator_factory.h" 66 67#if defined(OS_POSIX) 68#include <signal.h> 69#include "base/file_descriptor_posix.h" 70#include "remoting/host/posix/signal_handler.h" 71#endif // defined(OS_POSIX) 72 73#if defined(OS_MACOSX) 74#include "base/mac/scoped_cftyperef.h" 75#include "base/mac/scoped_nsautorelease_pool.h" 76#endif // defined(OS_MACOSX) 77 78#if defined(OS_LINUX) 79#include <pwd.h> 80#include "remoting/host/audio_capturer_linux.h" 81#include "remoting/host/pam_authorization_factory_posix.h" 82#endif // defined(OS_LINUX) 83 84// N.B. OS_WIN is defined by including src/base headers. 85#if defined(OS_WIN) 86#include <commctrl.h> 87#include "base/win/scoped_handle.h" 88#include "remoting/host/win/session_desktop_environment_factory.h" 89#endif // defined(OS_WIN) 90 91#if defined(TOOLKIT_GTK) 92#include "ui/gfx/gtk_util.h" 93#endif // defined(TOOLKIT_GTK) 94 95namespace { 96 97// This is used for tagging system event logs. 98const char kApplicationName[] = "chromoting"; 99 100// The command line switch used to get version of the daemon. 101const char kVersionSwitchName[] = "version"; 102 103// The command line switch used to pass name of the pipe to capture audio on 104// linux. 105const char kAudioPipeSwitchName[] = "audio-pipe-name"; 106 107void QuitMessageLoop(MessageLoop* message_loop) { 108 message_loop->PostTask(FROM_HERE, MessageLoop::QuitClosure()); 109} 110 111// Returns true if GetUsername() is implemented on this platform. 112bool CanGetUsername() { 113#if defined(OS_LINUX) 114 return true; 115#else // defined(OS_LINUX) 116 return false; 117#endif // defined(OS_LINUX) 118} // namespace 119 120// Returns the username associated with this process, or the empty string on 121// error. 122std::string GetUsername() { 123#if defined(OS_LINUX) 124 long buf_size = sysconf(_SC_GETPW_R_SIZE_MAX); 125 if (buf_size <= 0) 126 return ""; 127 scoped_array<char> buf(new char[buf_size]); 128 struct passwd passwd; 129 struct passwd* passwd_result = NULL; 130 getpwuid_r(getuid(), &passwd, buf.get(), buf_size, &passwd_result); 131 if (!passwd_result) 132 return ""; 133 return std::string(passwd_result->pw_name); 134#else // defined(OS_LINUX) 135 NOTREACHED(); 136 return ""; 137#endif // defined(OS_LINUX) 138} 139 140} // namespace 141 142namespace remoting { 143 144class HostProcess 145 : public ConfigFileWatcher::Delegate, 146 public HeartbeatSender::Listener, 147 public IPC::Listener { 148 public: 149 explicit HostProcess(scoped_ptr<ChromotingHostContext> context); 150 151 // Initializes IPC control channel and config file path from |cmd_line|. 152 bool InitWithCommandLine(const CommandLine* cmd_line); 153 154 // ConfigFileWatcher::Delegate interface. 155 virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE; 156 virtual void OnConfigWatcherError() OVERRIDE; 157 158 void StartWatchingConfigChanges(); 159 void CreateAuthenticatorFactory(); 160 161 // IPC::Listener implementation. 162 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 163 virtual void OnChannelError() OVERRIDE; 164 165 // HeartbeatSender::Listener overrides. 166 virtual void OnUnknownHostIdError() OVERRIDE; 167 168 void StartHostProcess(); 169 170 int get_exit_code() const; 171 172 private: 173#if defined(OS_POSIX) 174 // Registers a SIGTERM handler on the network thread, to shutdown the host. 175 void ListenForShutdownSignal(); 176 177 // Callback passed to RegisterSignalHandler() to handle SIGTERM events. 178 void SigTermHandler(int signal_number); 179#endif 180 181 // Asks the daemon to inject Secure Attention Sequence to the console. 182 void SendSasToConsole(); 183 184 void ShutdownHostProcess(); 185 186 // Applies the host config, returning true if successful. 187 bool ApplyConfig(scoped_ptr<JsonHostConfig> config); 188 189 void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); 190 bool OnHostDomainPolicyUpdate(const std::string& host_domain); 191 bool OnUsernamePolicyUpdate(bool username_match_required); 192 bool OnNatPolicyUpdate(bool nat_traversal_enabled); 193 bool OnCurtainPolicyUpdate(bool curtain_required); 194 bool OnHostTalkGadgetPrefixPolicyUpdate(const std::string& talkgadget_prefix); 195 196 void StartHost(); 197 198 void OnAuthFailed(); 199 200 void RejectAuthenticatingClient(); 201 202 // Invoked when the user uses the Disconnect windows to terminate 203 // the sessions, or when the local session is activated in curtain mode. 204 void OnDisconnectRequested(); 205 206 void RestartHost(); 207 208 void RestartOnHostShutdown(); 209 210 void Shutdown(int exit_code); 211 212 void OnShutdownFinished(); 213 214 void ResetHost(); 215 216 // Crashes the process in response to a daemon's request. The daemon passes 217 // the location of the code that detected the fatal error resulted in this 218 // request. 219 void OnCrash(const std::string& function_name, 220 const std::string& file_name, 221 const int& line_number); 222 223 scoped_ptr<ChromotingHostContext> context_; 224 scoped_ptr<IPC::ChannelProxy> daemon_channel_; 225 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; 226 227 FilePath host_config_path_; 228 scoped_ptr<ConfigFileWatcher> config_watcher_; 229 230 // Accessed on the network thread. 231 std::string host_id_; 232 protocol::SharedSecretHash host_secret_hash_; 233 HostKeyPair key_pair_; 234 std::string oauth_refresh_token_; 235 std::string serialized_config_; 236 std::string xmpp_login_; 237 std::string xmpp_auth_token_; 238 std::string xmpp_auth_service_; 239 240 scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_; 241 bool allow_nat_traversal_; 242 std::string talkgadget_prefix_; 243 244 scoped_ptr<CurtainMode> curtain_; 245 scoped_ptr<CurtainingHostObserver> curtaining_host_observer_; 246 247 bool restarting_; 248 bool shutting_down_; 249 250 scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_; 251 scoped_ptr<DesktopResizer> desktop_resizer_; 252 scoped_ptr<ResizingHostObserver> resizing_host_observer_; 253 scoped_ptr<XmppSignalStrategy> signal_strategy_; 254 scoped_ptr<SignalingConnector> signaling_connector_; 255 scoped_ptr<HeartbeatSender> heartbeat_sender_; 256 scoped_ptr<LogToServer> log_to_server_; 257 scoped_ptr<HostEventLogger> host_event_logger_; 258 259 scoped_ptr<HostUserInterface> host_user_interface_; 260 261 scoped_refptr<ChromotingHost> host_; 262 263#if defined(REMOTING_MULTI_PROCESS) 264 DesktopSessionConnector* desktop_session_connector_; 265#endif // defined(REMOTING_MULTI_PROCESS) 266 267 int exit_code_; 268}; 269 270HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context) 271 : context_(context.Pass()), 272 allow_nat_traversal_(true), 273 restarting_(false), 274 shutting_down_(false), 275 desktop_resizer_(DesktopResizer::Create()), 276#if defined(REMOTING_MULTI_PROCESS) 277 desktop_session_connector_(NULL), 278#endif // defined(REMOTING_MULTI_PROCESS) 279 exit_code_(kSuccessExitCode) { 280 network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); 281 curtain_ = CurtainMode::Create( 282 base::Bind(&HostProcess::OnDisconnectRequested, 283 base::Unretained(this)), 284 base::Bind(&HostProcess::RejectAuthenticatingClient, 285 base::Unretained(this))); 286} 287 288bool HostProcess::InitWithCommandLine(const CommandLine* cmd_line) { 289#if defined(REMOTING_MULTI_PROCESS) 290 // Parse the handle value and convert it to a handle/file descriptor. 291 std::string channel_name = 292 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName); 293 294 int pipe_handle = 0; 295 if (channel_name.empty() || 296 !base::StringToInt(channel_name, &pipe_handle)) { 297 LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName 298 << "' value: " << channel_name; 299 return false; 300 } 301 302#if defined(OS_WIN) 303 base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle)); 304 IPC::ChannelHandle channel_handle(pipe); 305#elif defined(OS_POSIX) 306 base::FileDescriptor pipe(pipe_handle, true); 307 IPC::ChannelHandle channel_handle(channel_name, pipe); 308#endif // defined(OS_POSIX) 309 310 // Connect to the daemon process. 311 daemon_channel_.reset(new IPC::ChannelProxy( 312 channel_handle, 313 IPC::Channel::MODE_CLIENT, 314 this, 315 context_->network_task_runner())); 316#else // !defined(REMOTING_MULTI_PROCESS) 317 // Connect to the daemon process. 318 std::string channel_name = 319 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName); 320 if (!channel_name.empty()) { 321 daemon_channel_.reset(new IPC::ChannelProxy( 322 channel_name, IPC::Channel::MODE_CLIENT, this, 323 context_->network_task_runner())); 324 } 325 326 FilePath default_config_dir = remoting::GetConfigDir(); 327 host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile); 328 if (cmd_line->HasSwitch(kHostConfigSwitchName)) { 329 host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName); 330 } 331#endif // !defined(REMOTING_MULTI_PROCESS) 332 333 return true; 334} 335 336void HostProcess::OnConfigUpdated( 337 const std::string& serialized_config) { 338 if (!context_->network_task_runner()->BelongsToCurrentThread()) { 339 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind( 340 &HostProcess::OnConfigUpdated, base::Unretained(this), 341 serialized_config)); 342 return; 343 } 344 345 // Filter out duplicates. 346 if (serialized_config_ == serialized_config) 347 return; 348 349 LOG(INFO) << "Processing new host configuration."; 350 351 serialized_config_ = serialized_config; 352 scoped_ptr<JsonHostConfig> config(new JsonHostConfig(FilePath())); 353 if (!config->SetSerializedData(serialized_config)) { 354 LOG(ERROR) << "Invalid configuration."; 355 Shutdown(kInvalidHostConfigurationExitCode); 356 return; 357 } 358 359 if (!ApplyConfig(config.Pass())) { 360 LOG(ERROR) << "Failed to apply the configuration."; 361 Shutdown(kInvalidHostConfigurationExitCode); 362 return; 363 } 364 365 // Start watching the policy (and eventually start the host) if this is 366 // the first configuration update. Otherwise, create new authenticator 367 // factory in case PIN has changed. 368 if (!policy_watcher_) { 369 policy_watcher_.reset( 370 policy_hack::PolicyWatcher::Create(context_->file_task_runner())); 371 policy_watcher_->StartWatching( 372 base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this))); 373 } else { 374 CreateAuthenticatorFactory(); 375 } 376} 377 378void HostProcess::OnConfigWatcherError() { 379 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 380 381 context_->network_task_runner()->PostTask( 382 FROM_HERE, 383 base::Bind(&HostProcess::Shutdown, base::Unretained(this), 384 kInvalidHostConfigurationExitCode)); 385} 386 387void HostProcess::StartWatchingConfigChanges() { 388#if !defined(REMOTING_MULTI_PROCESS) 389 // Start watching the host configuration file. 390 config_watcher_.reset(new ConfigFileWatcher(context_->ui_task_runner(), 391 context_->file_task_runner(), 392 this)); 393 config_watcher_->Watch(host_config_path_); 394#endif // !defined(REMOTING_MULTI_PROCESS) 395} 396 397#if defined(OS_POSIX) 398void HostProcess::ListenForShutdownSignal() { 399 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 400 401 remoting::RegisterSignalHandler( 402 SIGTERM, 403 base::Bind(&HostProcess::SigTermHandler, base::Unretained(this))); 404} 405 406void HostProcess::SigTermHandler(int signal_number) { 407 DCHECK(signal_number == SIGTERM); 408 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 409 LOG(INFO) << "Caught SIGTERM: Shutting down..."; 410 Shutdown(kSuccessExitCode); 411} 412#endif // OS_POSIX 413 414void HostProcess::CreateAuthenticatorFactory() { 415 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 416 417 if (!host_ || shutting_down_) 418 return; 419 420 std::string local_certificate = key_pair_.GenerateCertificate(); 421 if (local_certificate.empty()) { 422 LOG(ERROR) << "Failed to generate host certificate."; 423 Shutdown(kInitializationFailed); 424 return; 425 } 426 427 scoped_ptr<protocol::AuthenticatorFactory> factory( 428 new protocol::Me2MeHostAuthenticatorFactory( 429 local_certificate, *key_pair_.private_key(), host_secret_hash_)); 430#if defined(OS_LINUX) 431 // On Linux, perform a PAM authorization step after authentication. 432 factory.reset(new PamAuthorizationFactory(factory.Pass())); 433#endif 434 host_->SetAuthenticatorFactory(factory.Pass()); 435} 436 437// IPC::Listener implementation. 438bool HostProcess::OnMessageReceived(const IPC::Message& message) { 439 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 440 441#if defined(REMOTING_MULTI_PROCESS) 442 bool handled = true; 443 IPC_BEGIN_MESSAGE_MAP(HostProcess, message) 444 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Crash, 445 OnCrash) 446 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration, 447 OnConfigUpdated) 448 IPC_MESSAGE_FORWARD( 449 ChromotingDaemonNetworkMsg_DesktopAttached, 450 desktop_session_connector_, 451 DesktopSessionConnector::OnDesktopSessionAgentAttached) 452 IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected, 453 desktop_session_connector_, 454 DesktopSessionConnector::OnTerminalDisconnected) 455 IPC_MESSAGE_UNHANDLED(handled = false) 456 IPC_END_MESSAGE_MAP() 457 return handled; 458#else // !defined(REMOTING_MULTI_PROCESS) 459 return false; 460#endif // !defined(REMOTING_MULTI_PROCESS) 461} 462 463void HostProcess::OnChannelError() { 464 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 465 466 // Shutdown the host if the daemon disconnected the channel. 467 context_->network_task_runner()->PostTask( 468 FROM_HERE, 469 base::Bind(&HostProcess::Shutdown, base::Unretained(this), 470 kSuccessExitCode)); 471} 472 473void HostProcess::StartHostProcess() { 474 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 475 476 if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) { 477 OnConfigWatcherError(); 478 return; 479 } 480 481#if defined(OS_LINUX) 482 // TODO(sergeyu): Pass configuration parameters to the Linux-specific version 483 // of DesktopEnvironmentFactory when we have it. 484 remoting::VideoFrameCapturer::EnableXDamage(true); 485 486 // If an audio pipe is specific on the command-line then initialize 487 // AudioCapturerLinux to capture from it. 488 FilePath audio_pipe_name = CommandLine::ForCurrentProcess()-> 489 GetSwitchValuePath(kAudioPipeSwitchName); 490 if (!audio_pipe_name.empty()) { 491 remoting::AudioCapturerLinux::InitializePipeReader( 492 context_->audio_task_runner(), audio_pipe_name); 493 } 494#endif // defined(OS_LINUX) 495 496 // Create a desktop environment factory appropriate to the build type & 497 // platform. 498#if defined(OS_WIN) 499 500#if defined(REMOTING_MULTI_PROCESS) 501 IpcDesktopEnvironmentFactory* desktop_environment_factory = 502 new IpcDesktopEnvironmentFactory( 503 daemon_channel_.get(), 504 context_->input_task_runner(), 505 context_->network_task_runner(), 506 context_->ui_task_runner()); 507 desktop_session_connector_ = desktop_environment_factory; 508#else // !defined(REMOTING_MULTI_PROCESS) 509 DesktopEnvironmentFactory* desktop_environment_factory = 510 new SessionDesktopEnvironmentFactory( 511 context_->input_task_runner(), context_->ui_task_runner(), 512 base::Bind(&HostProcess::SendSasToConsole, base::Unretained(this))); 513#endif // !defined(REMOTING_MULTI_PROCESS) 514 515#else // !defined(OS_WIN) 516 DesktopEnvironmentFactory* desktop_environment_factory = 517 new DesktopEnvironmentFactory( 518 context_->input_task_runner(), context_->ui_task_runner()); 519#endif // !defined(OS_WIN) 520 521 desktop_environment_factory_.reset(desktop_environment_factory); 522 523#if defined(OS_POSIX) 524 context_->network_task_runner()->PostTask( 525 FROM_HERE, 526 base::Bind(&HostProcess::ListenForShutdownSignal, 527 base::Unretained(this))); 528#endif // OS_POSIX 529 530 // The host UI should be created on the UI thread. 531 bool want_user_interface = true; 532#if defined(OS_LINUX) 533 want_user_interface = false; 534#elif defined(OS_MACOSX) 535 // Don't try to display any UI on top of the system's login screen as this 536 // is rejected by the Window Server on OS X 10.7.4, and prevents the 537 // capturer from working (http://crbug.com/140984). 538 539 // TODO(lambroslambrou): Use a better technique of detecting whether we're 540 // running in the LoginWindow context, and refactor this into a separate 541 // function to be used here and in CurtainMode::ActivateCurtain(). 542 want_user_interface = getuid() != 0; 543#endif // OS_MACOSX 544 545 if (want_user_interface) { 546 host_user_interface_.reset( 547 new HostUserInterface(context_->network_task_runner(), 548 context_->ui_task_runner())); 549 host_user_interface_->Init(); 550 } 551 552 StartWatchingConfigChanges(); 553} 554 555int HostProcess::get_exit_code() const { 556 return exit_code_; 557} 558 559void HostProcess::SendSasToConsole() { 560 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 561 562 if (daemon_channel_) 563 daemon_channel_->Send(new ChromotingNetworkDaemonMsg_SendSasToConsole()); 564} 565 566void HostProcess::ShutdownHostProcess() { 567 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); 568 569 // Tear down resources that use ChromotingHostContext threads. 570 config_watcher_.reset(); 571 daemon_channel_.reset(); 572 desktop_environment_factory_.reset(); 573 host_user_interface_.reset(); 574 575 context_.reset(); 576} 577 578// Overridden from HeartbeatSender::Listener 579void HostProcess::OnUnknownHostIdError() { 580 LOG(ERROR) << "Host ID not found."; 581 Shutdown(kInvalidHostIdExitCode); 582} 583 584// Applies the host config, returning true if successful. 585bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) { 586 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 587 588 if (!config->GetString(kHostIdConfigPath, &host_id_)) { 589 LOG(ERROR) << "host_id is not defined in the config."; 590 return false; 591 } 592 593 if (!key_pair_.Load(*config)) { 594 return false; 595 } 596 597 std::string host_secret_hash_string; 598 if (!config->GetString(kHostSecretHashConfigPath, 599 &host_secret_hash_string)) { 600 host_secret_hash_string = "plain:"; 601 } 602 603 if (!host_secret_hash_.Parse(host_secret_hash_string)) { 604 LOG(ERROR) << "Invalid host_secret_hash."; 605 return false; 606 } 607 608 // Use an XMPP connection to the Talk network for session signalling. 609 if (!config->GetString(kXmppLoginConfigPath, &xmpp_login_) || 610 !(config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_) || 611 config->GetString(kOAuthRefreshTokenConfigPath, 612 &oauth_refresh_token_))) { 613 LOG(ERROR) << "XMPP credentials are not defined in the config."; 614 return false; 615 } 616 617 if (!oauth_refresh_token_.empty()) { 618 xmpp_auth_token_ = ""; // This will be set to the access token later. 619 xmpp_auth_service_ = "oauth2"; 620 } else if (!config->GetString(kXmppAuthServiceConfigPath, 621 &xmpp_auth_service_)) { 622 // For the me2me host, we default to ClientLogin token for chromiumsync 623 // because earlier versions of the host had no HTTP stack with which to 624 // request an OAuth2 access token. 625 xmpp_auth_service_ = kChromotingTokenDefaultServiceName; 626 } 627 return true; 628} 629 630void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) { 631 // TODO(rmsousa): Consolidate all On*PolicyUpdate methods into this one. 632 if (!context_->network_task_runner()->BelongsToCurrentThread()) { 633 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind( 634 &HostProcess::OnPolicyUpdate, base::Unretained(this), 635 base::Passed(&policies))); 636 return; 637 } 638 639 bool restart_required = false; 640 bool bool_value; 641 std::string string_value; 642 if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName, 643 &string_value)) { 644 restart_required |= OnHostDomainPolicyUpdate(string_value); 645 } 646 if (policies->GetBoolean( 647 policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName, 648 &bool_value)) { 649 restart_required |= OnUsernamePolicyUpdate(bool_value); 650 } 651 if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName, 652 &bool_value)) { 653 restart_required |= OnNatPolicyUpdate(bool_value); 654 } 655 if (policies->GetString( 656 policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName, 657 &string_value)) { 658 restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(string_value); 659 } 660 if (policies->GetBoolean( 661 policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName, 662 &bool_value)) { 663 restart_required |= OnCurtainPolicyUpdate(bool_value); 664 } 665 if (!host_) { 666 StartHost(); 667 } else if (restart_required) { 668 RestartHost(); 669 } 670} 671 672bool HostProcess::OnHostDomainPolicyUpdate(const std::string& host_domain) { 673 // Returns true if the host has to be restarted after this policy update. 674 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 675 676 if (!host_domain.empty() && 677 !EndsWith(xmpp_login_, std::string("@") + host_domain, false)) { 678 Shutdown(kInvalidHostDomainExitCode); 679 } 680 return false; 681} 682 683bool HostProcess::OnUsernamePolicyUpdate(bool host_username_match_required) { 684 // Returns false: never restart the host after this policy update. 685 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 686 687 if (host_username_match_required) { 688 if (!CanGetUsername() || 689 !StartsWithASCII(xmpp_login_, GetUsername() + std::string("@"), 690 false)) { 691 Shutdown(kUsernameMismatchExitCode); 692 } 693 } 694 return false; 695} 696 697bool HostProcess::OnNatPolicyUpdate(bool nat_traversal_enabled) { 698 // Returns true if the host has to be restarted after this policy update. 699 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 700 701 if (allow_nat_traversal_ != nat_traversal_enabled) { 702 allow_nat_traversal_ = nat_traversal_enabled; 703 LOG(INFO) << "Updated NAT policy."; 704 return true; 705 } 706 return false; 707} 708 709bool HostProcess::OnCurtainPolicyUpdate(bool curtain_required) { 710 // Returns true if the host has to be restarted after this policy update. 711 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 712 713#if defined(OS_MACOSX) 714 if (curtain_required) { 715 // If curtain mode is required, then we can't currently support remoting 716 // the login screen. This is because we don't curtain the login screen 717 // and the current daemon architecture means that the connction is closed 718 // immediately after login, leaving the host system uncurtained. 719 // 720 // TODO(jamiewalch): Fix this once we have implemented the multi-process 721 // daemon architecture (crbug.com/134894) 722 if (getuid() == 0) { 723 Shutdown(kLoginScreenNotSupportedExitCode); 724 return false; 725 } 726 } 727#endif 728 if (curtain_->required() != curtain_required) { 729 LOG(INFO) << "Updated curtain policy."; 730 curtain_->set_required(curtain_required); 731 return true; 732 } 733 return false; 734} 735 736bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate( 737 const std::string& talkgadget_prefix) { 738 // Returns true if the host has to be restarted after this policy update. 739 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 740 741 if (talkgadget_prefix != talkgadget_prefix_) { 742 LOG(INFO) << "Updated talkgadget policy."; 743 talkgadget_prefix_ = talkgadget_prefix; 744 return true; 745 } 746 return false; 747} 748 749void HostProcess::StartHost() { 750 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 751 DCHECK(!host_); 752 DCHECK(!signal_strategy_.get()); 753 754 if (shutting_down_) 755 return; 756 757 signal_strategy_.reset( 758 new XmppSignalStrategy(context_->url_request_context_getter(), 759 xmpp_login_, xmpp_auth_token_, 760 xmpp_auth_service_)); 761 762 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker( 763 new DnsBlackholeChecker(context_->url_request_context_getter(), 764 talkgadget_prefix_)); 765 766 signaling_connector_.reset(new SignalingConnector( 767 signal_strategy_.get(), 768 context_->url_request_context_getter(), 769 dns_blackhole_checker.Pass(), 770 base::Bind(&HostProcess::OnAuthFailed, base::Unretained(this)))); 771 772 if (!oauth_refresh_token_.empty()) { 773 scoped_ptr<SignalingConnector::OAuthCredentials> oauth_credentials( 774 new SignalingConnector::OAuthCredentials( 775 xmpp_login_, oauth_refresh_token_)); 776 signaling_connector_->EnableOAuth(oauth_credentials.Pass()); 777 } 778 779 NetworkSettings network_settings( 780 allow_nat_traversal_ ? 781 NetworkSettings::NAT_TRAVERSAL_ENABLED : 782 NetworkSettings::NAT_TRAVERSAL_DISABLED); 783 if (!allow_nat_traversal_) { 784 network_settings.min_port = NetworkSettings::kDefaultMinPort; 785 network_settings.max_port = NetworkSettings::kDefaultMaxPort; 786 } 787 788 host_ = new ChromotingHost( 789 signal_strategy_.get(), 790 desktop_environment_factory_.get(), 791 CreateHostSessionManager(network_settings, 792 context_->url_request_context_getter()), 793 context_->audio_task_runner(), 794 context_->capture_task_runner(), 795 context_->encode_task_runner(), 796 context_->network_task_runner()); 797 798 // TODO(simonmorris): Get the maximum session duration from a policy. 799#if defined(OS_LINUX) 800 host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20)); 801#endif 802 803 heartbeat_sender_.reset(new HeartbeatSender( 804 this, host_id_, signal_strategy_.get(), &key_pair_)); 805 806 log_to_server_.reset( 807 new LogToServer(host_, ServerLogEntry::ME2ME, signal_strategy_.get())); 808 host_event_logger_ = HostEventLogger::Create(host_, kApplicationName); 809 810#if defined(OS_LINUX) 811 // Desktop resizing is implemented on all three platforms, but may not be 812 // the right thing to do for non-virtual desktops. Disable it until we can 813 // implement a configuration UI. 814 resizing_host_observer_.reset( 815 new ResizingHostObserver(desktop_resizer_.get(), host_)); 816#endif 817 818 // Curtain mode is currently broken on Mac (the only supported platform), 819 // so it's disabled until we've had time to fully investigate. 820 // curtaining_host_observer_.reset(new CurtainingHostObserver( 821 // curtain_.get(), host_)); 822 823 if (host_user_interface_.get()) { 824 host_user_interface_->Start( 825 host_, base::Bind(&HostProcess::OnDisconnectRequested, 826 base::Unretained(this))); 827 } 828 829 host_->Start(xmpp_login_); 830 831 CreateAuthenticatorFactory(); 832} 833 834void HostProcess::OnAuthFailed() { 835 Shutdown(kInvalidOauthCredentialsExitCode); 836} 837 838void HostProcess::RejectAuthenticatingClient() { 839 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 840 DCHECK(host_); 841 host_->RejectAuthenticatingClient(); 842} 843 844// Invoked when the user uses the Disconnect windows to terminate 845// the sessions, or when the local session is activated in curtain mode. 846void HostProcess::OnDisconnectRequested() { 847 if (!context_->network_task_runner()->BelongsToCurrentThread()) { 848 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind( 849 &HostProcess::OnDisconnectRequested, base::Unretained(this))); 850 return; 851 } 852 if (host_) { 853 host_->DisconnectAllClients(); 854 } 855} 856 857void HostProcess::RestartHost() { 858 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 859 860 if (restarting_ || shutting_down_) 861 return; 862 863 restarting_ = true; 864 host_->Shutdown(base::Bind( 865 &HostProcess::RestartOnHostShutdown, base::Unretained(this))); 866} 867 868void HostProcess::RestartOnHostShutdown() { 869 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 870 871 if (shutting_down_) 872 return; 873 874 restarting_ = false; 875 host_ = NULL; 876 ResetHost(); 877 878 StartHost(); 879} 880 881void HostProcess::Shutdown(int exit_code) { 882 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 883 884 if (shutting_down_) 885 return; 886 887 shutting_down_ = true; 888 exit_code_ = exit_code; 889 if (host_) { 890 host_->Shutdown(base::Bind( 891 &HostProcess::OnShutdownFinished, base::Unretained(this))); 892 } else { 893 OnShutdownFinished(); 894 } 895} 896 897void HostProcess::OnShutdownFinished() { 898 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 899 900 // Destroy networking objects while we are on the network thread. 901 host_ = NULL; 902 ResetHost(); 903 904 if (policy_watcher_.get()) { 905 base::WaitableEvent done_event(true, false); 906 policy_watcher_->StopWatching(&done_event); 907 done_event.Wait(); 908 policy_watcher_.reset(); 909 } 910 911 // Complete the rest of shutdown on the main thread. 912 context_->ui_task_runner()->PostTask( 913 FROM_HERE, 914 base::Bind(&HostProcess::ShutdownHostProcess, 915 base::Unretained(this))); 916} 917 918void HostProcess::ResetHost() { 919 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); 920 921 curtaining_host_observer_.reset(); 922 host_event_logger_.reset(); 923 log_to_server_.reset(); 924 heartbeat_sender_.reset(); 925 signaling_connector_.reset(); 926 signal_strategy_.reset(); 927 resizing_host_observer_.reset(); 928} 929 930void HostProcess::OnCrash(const std::string& function_name, 931 const std::string& file_name, 932 const int& line_number) { 933 CHECK(false); 934} 935 936} // namespace remoting 937 938int main(int argc, char** argv) { 939#if defined(OS_MACOSX) 940 // Needed so we don't leak objects when threads are created. 941 base::mac::ScopedNSAutoreleasePool pool; 942#endif 943 944 CommandLine::Init(argc, argv); 945 946 // This object instance is required by Chrome code (for example, 947 // LazyInstance, MessageLoop). 948 base::AtExitManager exit_manager; 949 950 if (CommandLine::ForCurrentProcess()->HasSwitch(kVersionSwitchName)) { 951 printf("%s\n", STRINGIZE(VERSION)); 952 return 0; 953 } 954 955 remoting::InitHostLogging(); 956 957#if defined(TOOLKIT_GTK) 958 // Required for any calls into GTK functions, such as the Disconnect and 959 // Continue windows, though these should not be used for the Me2Me case 960 // (crbug.com/104377). 961 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 962 gfx::GtkInitFromCommandLine(*cmd_line); 963#endif // TOOLKIT_GTK 964 965 // Enable support for SSL server sockets, which must be done while still 966 // single-threaded. 967 net::EnableSSLServerSockets(); 968 969 // Create the main message loop and start helper threads. 970 MessageLoop message_loop(MessageLoop::TYPE_UI); 971 base::Closure quit_message_loop = base::Bind(&QuitMessageLoop, &message_loop); 972 scoped_ptr<remoting::ChromotingHostContext> context( 973 new remoting::ChromotingHostContext( 974 new remoting::AutoThreadTaskRunner(message_loop.message_loop_proxy(), 975 quit_message_loop))); 976 977 if (!context->Start()) 978 return remoting::kInitializationFailed; 979 980 // Create the host process instance and enter the main message loop. 981 remoting::HostProcess me2me_host(context.Pass()); 982 me2me_host.StartHostProcess(); 983 message_loop.Run(); 984 return me2me_host.get_exit_code(); 985} 986 987#if defined(OS_WIN) 988HMODULE g_hModule = NULL; 989 990int CALLBACK WinMain(HINSTANCE instance, 991 HINSTANCE previous_instance, 992 LPSTR command_line, 993 int show_command) { 994#ifdef OFFICIAL_BUILD 995 if (remoting::IsUsageStatsAllowed()) { 996 remoting::InitializeCrashReporting(); 997 } 998#endif // OFFICIAL_BUILD 999 1000 g_hModule = instance; 1001 1002 // Register and initialize common controls. 1003 INITCOMMONCONTROLSEX info; 1004 info.dwSize = sizeof(info); 1005 info.dwICC = ICC_STANDARD_CLASSES; 1006 InitCommonControlsEx(&info); 1007 1008 // Mark the process as DPI-aware, so Windows won't scale coordinates in APIs. 1009 // N.B. This API exists on Vista and above. 1010 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { 1011 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16("user32"))); 1012 base::ScopedNativeLibrary user32(path); 1013 CHECK(user32.is_valid()); 1014 1015 typedef BOOL (WINAPI * SetProcessDPIAwareFn)(); 1016 SetProcessDPIAwareFn set_process_dpi_aware = 1017 static_cast<SetProcessDPIAwareFn>( 1018 user32.GetFunctionPointer("SetProcessDPIAware")); 1019 set_process_dpi_aware(); 1020 } 1021 1022 // CommandLine::Init() ignores the passed |argc| and |argv| on Windows getting 1023 // the command line from GetCommandLineW(), so we can safely pass NULL here. 1024 return main(0, NULL); 1025} 1026 1027#endif // defined(OS_WIN) 1028