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