This source file includes following definitions.
- InitWithCommandLine
- OnConfigUpdated
- OnConfigWatcherError
- StartOnNetworkThread
- SigTermHandler
- CreateAuthenticatorFactory
- OnMessageReceived
- OnChannelError
- StartOnUiThread
- ShutdownOnUiThread
- OnUnknownHostIdError
- OnHeartbeatSuccessful
- OnHostDeleted
- OnInitializePairingRegistry
- ApplyConfig
- OnPolicyUpdate
- OnHostDomainPolicyUpdate
- OnUsernamePolicyUpdate
- OnNatPolicyUpdate
- OnCurtainPolicyUpdate
- OnHostTalkGadgetPrefixPolicyUpdate
- OnHostTokenUrlPolicyUpdate
- OnPairingPolicyUpdate
- OnGnubbyAuthPolicyUpdate
- StartHost
- OnAuthFailed
- RestartHost
- ShutdownHost
- ScheduleHostShutdown
- ShutdownOnNetworkThread
- OnCrash
- HostProcessMain
- main
#include <string>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/alias.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "crypto/nss_util.h"
#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_listener.h"
#include "media/base/media.h"
#include "net/base/network_change_notifier.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/ssl_server_socket.h"
#include "net/url_request/url_fetcher.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/breakpad.h"
#include "remoting/base/constants.h"
#include "remoting/base/logging.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/host/branding.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/chromoting_messages.h"
#include "remoting/host/config_file_watcher.h"
#include "remoting/host/config_watcher.h"
#include "remoting/host/desktop_environment.h"
#include "remoting/host/desktop_session_connector.h"
#include "remoting/host/dns_blackhole_checker.h"
#include "remoting/host/heartbeat_sender.h"
#include "remoting/host/host_change_notification_listener.h"
#include "remoting/host/host_config.h"
#include "remoting/host/host_event_logger.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/host_main.h"
#include "remoting/host/host_status_sender.h"
#include "remoting/host/ipc_constants.h"
#include "remoting/host/ipc_desktop_environment.h"
#include "remoting/host/ipc_host_event_logger.h"
#include "remoting/host/json_host_config.h"
#include "remoting/host/log_to_server.h"
#include "remoting/host/logging.h"
#include "remoting/host/me2me_desktop_environment.h"
#include "remoting/host/pairing_registry_delegate.h"
#include "remoting/host/policy_hack/policy_watcher.h"
#include "remoting/host/service_urls.h"
#include "remoting/host/session_manager_factory.h"
#include "remoting/host/signaling_connector.h"
#include "remoting/host/token_validator_factory_impl.h"
#include "remoting/host/usage_stats_consent.h"
#include "remoting/host/username.h"
#include "remoting/jingle_glue/network_settings.h"
#include "remoting/jingle_glue/xmpp_signal_strategy.h"
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#include "remoting/protocol/pairing_registry.h"
#include "remoting/protocol/token_validator.h"
#if defined(OS_POSIX)
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/file_descriptor_posix.h"
#include "remoting/host/pam_authorization_factory_posix.h"
#include "remoting/host/posix/signal_handler.h"
#endif
#if defined(OS_MACOSX)
#include "base/mac/scoped_cftyperef.h"
#endif
#if defined(OS_LINUX)
#include "remoting/host/audio_capturer_linux.h"
#endif
#if defined(OS_WIN)
#include <commctrl.h>
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "remoting/host/pairing_registry_delegate_win.h"
#include "remoting/host/win/session_desktop_environment.h"
#endif
#if defined(TOOLKIT_GTK)
#include "ui/gfx/gtk_util.h"
#endif
using remoting::protocol::PairingRegistry;
namespace {
const char kApplicationName[] = "chromoting";
#if defined(OS_LINUX)
const char kAudioPipeSwitchName[] = "audio-pipe-name";
const char kAuthSocknameSwitchName[] = "ssh-auth-sockname";
#endif
const char kSignalParentSwitchName[] = "signal-parent";
const char kStdinConfigPath[] = "-";
}
namespace remoting {
class HostProcess
: public ConfigWatcher::Delegate,
public HeartbeatSender::Listener,
public HostChangeNotificationListener::Listener,
public IPC::Listener,
public base::RefCountedThreadSafe<HostProcess> {
public:
HostProcess(scoped_ptr<ChromotingHostContext> context,
int* exit_code_out);
virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE;
virtual void OnConfigWatcherError() OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
virtual void OnChannelError() OVERRIDE;
virtual void OnHeartbeatSuccessful() OVERRIDE;
virtual void OnUnknownHostIdError() OVERRIDE;
virtual void OnHostDeleted() OVERRIDE;
void OnInitializePairingRegistry(
IPC::PlatformFileForTransit privileged_key,
IPC::PlatformFileForTransit unprivileged_key);
private:
enum HostState {
HOST_INITIALIZING,
HOST_STARTED,
HOST_STOPPING_TO_RESTART,
HOST_STOPPING,
HOST_STOPPED,
};
friend class base::RefCountedThreadSafe<HostProcess>;
virtual ~HostProcess();
void StartOnNetworkThread();
#if defined(OS_POSIX)
void SigTermHandler(int signal_number);
#endif
void StartOnUiThread();
bool InitWithCommandLine(const CommandLine* cmd_line);
void StartWatchingConfigChanges();
void CreateAuthenticatorFactory();
void ShutdownOnUiThread();
bool ApplyConfig(scoped_ptr<JsonHostConfig> config);
void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
bool OnHostDomainPolicyUpdate(const std::string& host_domain);
bool OnUsernamePolicyUpdate(bool curtain_required,
bool username_match_required);
bool OnNatPolicyUpdate(bool nat_traversal_enabled);
void OnCurtainPolicyUpdate(bool curtain_required);
bool OnHostTalkGadgetPrefixPolicyUpdate(const std::string& talkgadget_prefix);
bool OnHostTokenUrlPolicyUpdate(
const GURL& token_url,
const GURL& token_validation_url,
const std::string& token_validation_cert_issuer);
bool OnPairingPolicyUpdate(bool pairing_enabled);
bool OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth);
void StartHost();
void OnAuthFailed();
void RestartHost();
void ShutdownHost(HostExitCodes exit_code);
void ScheduleHostShutdown();
void ShutdownOnNetworkThread();
void OnCrash(const std::string& function_name,
const std::string& file_name,
const int& line_number);
scoped_ptr<ChromotingHostContext> context_;
scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
scoped_ptr<IPC::ChannelProxy> daemon_channel_;
XmppSignalStrategy::XmppServerConfig xmpp_server_config_;
std::string directory_bot_jid_;
base::FilePath host_config_path_;
std::string host_config_;
scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
HostState state_;
scoped_ptr<ConfigWatcher> config_watcher_;
std::string host_id_;
protocol::SharedSecretHash host_secret_hash_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string oauth_refresh_token_;
std::string serialized_config_;
std::string host_owner_;
bool use_service_account_;
scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
bool allow_nat_traversal_;
std::string talkgadget_prefix_;
bool allow_pairing_;
bool curtain_required_;
ThirdPartyAuthConfig third_party_auth_config_;
bool enable_gnubby_auth_;
scoped_ptr<OAuthTokenGetter> oauth_token_getter_;
scoped_ptr<XmppSignalStrategy> signal_strategy_;
scoped_ptr<SignalingConnector> signaling_connector_;
scoped_ptr<HeartbeatSender> heartbeat_sender_;
scoped_ptr<HostStatusSender> host_status_sender_;
scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_;
scoped_ptr<LogToServer> log_to_server_;
scoped_ptr<HostEventLogger> host_event_logger_;
scoped_ptr<ChromotingHost> host_;
scoped_refptr<HostProcess> self_;
#if defined(REMOTING_MULTI_PROCESS)
DesktopSessionConnector* desktop_session_connector_;
#endif
int* exit_code_out_;
bool signal_parent_;
scoped_ptr<PairingRegistry::Delegate> pairing_registry_delegate_;
};
HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
int* exit_code_out)
: context_(context.Pass()),
state_(HOST_INITIALIZING),
use_service_account_(false),
allow_nat_traversal_(true),
allow_pairing_(true),
curtain_required_(false),
enable_gnubby_auth_(false),
#if defined(REMOTING_MULTI_PROCESS)
desktop_session_connector_(NULL),
#endif
self_(this),
exit_code_out_(exit_code_out),
signal_parent_(false) {
StartOnUiThread();
}
HostProcess::~HostProcess() {
DCHECK(!config_watcher_);
DCHECK(!daemon_channel_);
DCHECK(!desktop_environment_factory_);
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
context_->ui_task_runner();
task_runner->DeleteSoon(FROM_HERE, context_.release());
}
bool HostProcess::InitWithCommandLine(const CommandLine* cmd_line) {
#if defined(REMOTING_MULTI_PROCESS)
std::string channel_name =
cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
int pipe_handle = 0;
if (channel_name.empty() ||
!base::StringToInt(channel_name, &pipe_handle)) {
LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
<< "' value: " << channel_name;
return false;
}
#if defined(OS_WIN)
base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
IPC::ChannelHandle channel_handle(pipe);
#elif defined(OS_POSIX)
base::FileDescriptor pipe(pipe_handle, true);
IPC::ChannelHandle channel_handle(channel_name, pipe);
#endif
daemon_channel_.reset(new IPC::ChannelProxy(
channel_handle,
IPC::Channel::MODE_CLIENT,
this,
context_->network_task_runner()));
#else
std::string channel_name =
cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
if (!channel_name.empty()) {
daemon_channel_.reset(
new IPC::ChannelProxy(channel_name,
IPC::Channel::MODE_CLIENT,
this,
context_->network_task_runner().get()));
}
if (cmd_line->HasSwitch(kHostConfigSwitchName)) {
host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName);
if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
char buf[4096];
size_t len;
while ((len = fread(buf, 1, sizeof(buf), stdin)) > 0) {
host_config_.append(buf, len);
}
}
} else {
base::FilePath default_config_dir = remoting::GetConfigDir();
host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile);
}
if (host_config_path_ != base::FilePath(kStdinConfigPath) &&
!base::PathExists(host_config_path_)) {
LOG(ERROR) << "Can't find host config at " << host_config_path_.value();
return false;
}
#endif
net::URLFetcher::SetIgnoreCertificateRequests(true);
ServiceUrls* service_urls = ServiceUrls::GetInstance();
bool xmpp_server_valid = net::ParseHostAndPort(
service_urls->xmpp_server_address(),
&xmpp_server_config_.host, &xmpp_server_config_.port);
if (!xmpp_server_valid) {
LOG(ERROR) << "Invalid XMPP server: " <<
service_urls->xmpp_server_address();
return false;
}
xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
directory_bot_jid_ = service_urls->directory_bot_jid();
signal_parent_ = cmd_line->HasSwitch(kSignalParentSwitchName);
return true;
}
void HostProcess::OnConfigUpdated(
const std::string& serialized_config) {
if (!context_->network_task_runner()->BelongsToCurrentThread()) {
context_->network_task_runner()->PostTask(FROM_HERE,
base::Bind(&HostProcess::OnConfigUpdated, this, serialized_config));
return;
}
if (serialized_config_ == serialized_config)
return;
HOST_LOG << "Processing new host configuration.";
serialized_config_ = serialized_config;
scoped_ptr<JsonHostConfig> config(new JsonHostConfig(base::FilePath()));
if (!config->SetSerializedData(serialized_config)) {
LOG(ERROR) << "Invalid configuration.";
ShutdownHost(kInvalidHostConfigurationExitCode);
return;
}
if (!ApplyConfig(config.Pass())) {
LOG(ERROR) << "Failed to apply the configuration.";
ShutdownHost(kInvalidHostConfigurationExitCode);
return;
}
if (state_ == HOST_INITIALIZING) {
policy_watcher_.reset(
policy_hack::PolicyWatcher::Create(context_->file_task_runner()));
policy_watcher_->StartWatching(
base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this)));
} else if (state_ == HOST_STARTED) {
CreateAuthenticatorFactory();
}
}
void HostProcess::OnConfigWatcherError() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
ShutdownHost(kInvalidHostConfigurationExitCode);
}
void HostProcess::StartOnNetworkThread() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
#if !defined(REMOTING_MULTI_PROCESS)
if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
OnConfigUpdated(host_config_);
} else {
config_watcher_.reset(new ConfigFileWatcher(context_->network_task_runner(),
context_->file_task_runner(),
host_config_path_));
config_watcher_->Watch(this);
}
#endif
#if defined(OS_POSIX)
remoting::RegisterSignalHandler(
SIGTERM,
base::Bind(&HostProcess::SigTermHandler, base::Unretained(this)));
#endif
}
#if defined(OS_POSIX)
void HostProcess::SigTermHandler(int signal_number) {
DCHECK(signal_number == SIGTERM);
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
HOST_LOG << "Caught SIGTERM: Shutting down...";
ShutdownHost(kSuccessExitCode);
}
#endif
void HostProcess::CreateAuthenticatorFactory() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (state_ != HOST_STARTED)
return;
std::string local_certificate = key_pair_->GenerateCertificate();
if (local_certificate.empty()) {
LOG(ERROR) << "Failed to generate host certificate.";
ShutdownHost(kInitializationFailed);
return;
}
scoped_refptr<PairingRegistry> pairing_registry = NULL;
if (allow_pairing_) {
if (!pairing_registry_delegate_)
pairing_registry_delegate_ = CreatePairingRegistryDelegate();
if (pairing_registry_delegate_) {
pairing_registry = new PairingRegistry(context_->file_task_runner(),
pairing_registry_delegate_.Pass());
}
}
scoped_ptr<protocol::AuthenticatorFactory> factory;
if (third_party_auth_config_.is_empty()) {
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
use_service_account_, host_owner_, local_certificate, key_pair_,
host_secret_hash_, pairing_registry);
} else if (third_party_auth_config_.is_valid()) {
scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory(
new TokenValidatorFactoryImpl(
third_party_auth_config_,
key_pair_, context_->url_request_context_getter()));
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
use_service_account_, host_owner_, local_certificate, key_pair_,
token_validator_factory.Pass());
} else {
LOG(ERROR) << "One of the third-party token URLs is empty or invalid. "
<< "Host will reject all clients until policies are corrected. "
<< "TokenUrl: " << third_party_auth_config_.token_url << ", "
<< "TokenValidationUrl: "
<< third_party_auth_config_.token_validation_url;
factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting();
}
#if defined(OS_POSIX)
factory.reset(new PamAuthorizationFactory(factory.Pass()));
#endif
host_->SetAuthenticatorFactory(factory.Pass());
host_->set_pairing_registry(pairing_registry);
}
bool HostProcess::OnMessageReceived(const IPC::Message& message) {
DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
#if defined(REMOTING_MULTI_PROCESS)
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(HostProcess, message)
IPC_MESSAGE_HANDLER(ChromotingDaemonMsg_Crash, OnCrash)
IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration,
OnConfigUpdated)
IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_InitializePairingRegistry,
OnInitializePairingRegistry)
IPC_MESSAGE_FORWARD(
ChromotingDaemonNetworkMsg_DesktopAttached,
desktop_session_connector_,
DesktopSessionConnector::OnDesktopSessionAgentAttached)
IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected,
desktop_session_connector_,
DesktopSessionConnector::OnTerminalDisconnected)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
CHECK(handled) << "Received unexpected IPC type: " << message.type();
return handled;
#else
return false;
#endif
}
void HostProcess::OnChannelError() {
DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
context_->network_task_runner()->PostTask(
FROM_HERE,
base::Bind(&HostProcess::ShutdownHost, this, kSuccessExitCode));
}
void HostProcess::StartOnUiThread() {
DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) {
context_->network_task_runner()->PostTask(
FROM_HERE, base::Bind(&HostProcess::ShutdownHost, this,
kUsageExitCode));
return;
}
#if defined(OS_LINUX)
base::FilePath audio_pipe_name = CommandLine::ForCurrentProcess()->
GetSwitchValuePath(kAudioPipeSwitchName);
if (!audio_pipe_name.empty()) {
remoting::AudioCapturerLinux::InitializePipeReader(
context_->audio_task_runner(), audio_pipe_name);
}
base::FilePath gnubby_socket_name = CommandLine::ForCurrentProcess()->
GetSwitchValuePath(kAuthSocknameSwitchName);
if (!gnubby_socket_name.empty())
remoting::GnubbyAuthHandler::SetGnubbySocketName(gnubby_socket_name);
#endif
#if defined(OS_WIN)
IpcDesktopEnvironmentFactory* desktop_environment_factory =
new IpcDesktopEnvironmentFactory(
context_->audio_task_runner(),
context_->network_task_runner(),
context_->video_capture_task_runner(),
context_->network_task_runner(),
daemon_channel_.get());
desktop_session_connector_ = desktop_environment_factory;
#else
DesktopEnvironmentFactory* desktop_environment_factory =
new Me2MeDesktopEnvironmentFactory(
context_->network_task_runner(),
context_->input_task_runner(),
context_->ui_task_runner());
#endif
desktop_environment_factory_.reset(desktop_environment_factory);
desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth_);
context_->network_task_runner()->PostTask(
FROM_HERE,
base::Bind(&HostProcess::StartOnNetworkThread, this));
}
void HostProcess::ShutdownOnUiThread() {
DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
network_change_notifier_.reset();
daemon_channel_.reset();
desktop_environment_factory_.reset();
self_ = NULL;
#if defined(OS_LINUX)
AudioCapturerLinux::InitializePipeReader(NULL, base::FilePath());
#endif
}
void HostProcess::OnUnknownHostIdError() {
LOG(ERROR) << "Host ID not found.";
ShutdownHost(kInvalidHostIdExitCode);
}
void HostProcess::OnHeartbeatSuccessful() {
HOST_LOG << "Host ready to receive connections.";
#if defined(OS_POSIX)
if (signal_parent_) {
kill(getppid(), SIGUSR1);
signal_parent_ = false;
}
#endif
}
void HostProcess::OnHostDeleted() {
LOG(ERROR) << "Host was deleted from the directory.";
ShutdownHost(kInvalidHostIdExitCode);
}
void HostProcess::OnInitializePairingRegistry(
IPC::PlatformFileForTransit privileged_key,
IPC::PlatformFileForTransit unprivileged_key) {
DCHECK(!pairing_registry_delegate_);
#if defined(OS_WIN)
scoped_ptr<PairingRegistryDelegateWin> delegate(
new PairingRegistryDelegateWin());
bool result = delegate->SetRootKeys(
reinterpret_cast<HKEY>(
IPC::PlatformFileForTransitToPlatformFile(privileged_key)),
reinterpret_cast<HKEY>(
IPC::PlatformFileForTransitToPlatformFile(unprivileged_key)));
if (!result)
return;
pairing_registry_delegate_ = delegate.PassAs<PairingRegistry::Delegate>();
#else
NOTREACHED();
#endif
}
bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (!config->GetString(kHostIdConfigPath, &host_id_)) {
LOG(ERROR) << "host_id is not defined in the config.";
return false;
}
std::string key_base64;
if (!config->GetString(kPrivateKeyConfigPath, &key_base64)) {
LOG(ERROR) << "Private key couldn't be read from the config file.";
return false;
}
key_pair_ = RsaKeyPair::FromString(key_base64);
if (!key_pair_.get()) {
LOG(ERROR) << "Invalid private key in the config file.";
return false;
}
std::string host_secret_hash_string;
if (!config->GetString(kHostSecretHashConfigPath,
&host_secret_hash_string)) {
host_secret_hash_string = "plain:";
}
if (!host_secret_hash_.Parse(host_secret_hash_string)) {
LOG(ERROR) << "Invalid host_secret_hash.";
return false;
}
if (!config->GetString(kXmppLoginConfigPath, &xmpp_server_config_.username) ||
!(config->GetString(kXmppAuthTokenConfigPath,
&xmpp_server_config_.auth_token) ||
config->GetString(kOAuthRefreshTokenConfigPath,
&oauth_refresh_token_))) {
LOG(ERROR) << "XMPP credentials are not defined in the config.";
return false;
}
if (!oauth_refresh_token_.empty()) {
xmpp_server_config_.auth_token = "";
xmpp_server_config_.auth_service = "oauth2";
} else if (!config->GetString(kXmppAuthServiceConfigPath,
&xmpp_server_config_.auth_service)) {
xmpp_server_config_.auth_service = kChromotingTokenDefaultServiceName;
}
if (config->GetString(kHostOwnerConfigPath, &host_owner_)) {
use_service_account_ = true;
} else {
host_owner_ = xmpp_server_config_.username;
use_service_account_ = false;
}
return true;
}
void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
if (!context_->network_task_runner()->BelongsToCurrentThread()) {
context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
&HostProcess::OnPolicyUpdate, this, base::Passed(&policies)));
return;
}
bool restart_required = false;
bool bool_value;
std::string string_value;
if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
&string_value)) {
restart_required |= OnHostDomainPolicyUpdate(string_value);
}
bool curtain_required = false;
if (policies->GetBoolean(
policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
&curtain_required)) {
OnCurtainPolicyUpdate(curtain_required);
}
if (policies->GetBoolean(
policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName,
&bool_value)) {
restart_required |= OnUsernamePolicyUpdate(curtain_required, bool_value);
}
if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
&bool_value)) {
restart_required |= OnNatPolicyUpdate(bool_value);
}
if (policies->GetString(
policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
&string_value)) {
restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(string_value);
}
std::string token_url_string, token_validation_url_string;
std::string token_validation_cert_issuer;
if (policies->GetString(
policy_hack::PolicyWatcher::kHostTokenUrlPolicyName,
&token_url_string) &&
policies->GetString(
policy_hack::PolicyWatcher::kHostTokenValidationUrlPolicyName,
&token_validation_url_string) &&
policies->GetString(
policy_hack::PolicyWatcher::kHostTokenValidationCertIssuerPolicyName,
&token_validation_cert_issuer)) {
restart_required |= OnHostTokenUrlPolicyUpdate(
GURL(token_url_string), GURL(token_validation_url_string),
token_validation_cert_issuer);
}
if (policies->GetBoolean(
policy_hack::PolicyWatcher::kHostAllowClientPairing,
&bool_value)) {
restart_required |= OnPairingPolicyUpdate(bool_value);
}
if (policies->GetBoolean(
policy_hack::PolicyWatcher::kHostAllowGnubbyAuthPolicyName,
&bool_value))
restart_required |= OnGnubbyAuthPolicyUpdate(bool_value);
if (state_ == HOST_INITIALIZING) {
StartHost();
} else if (state_ == HOST_STARTED && restart_required) {
RestartHost();
}
}
bool HostProcess::OnHostDomainPolicyUpdate(const std::string& host_domain) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
HOST_LOG << "Policy sets host domain: " << host_domain;
if (!host_domain.empty() &&
!EndsWith(host_owner_, std::string("@") + host_domain, false)) {
ShutdownHost(kInvalidHostDomainExitCode);
}
return false;
}
bool HostProcess::OnUsernamePolicyUpdate(bool curtain_required,
bool host_username_match_required) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (host_username_match_required) {
HOST_LOG << "Policy requires host username match.";
std::string username = GetUsername();
bool shutdown = username.empty() ||
!StartsWithASCII(host_owner_, username + std::string("@"),
false);
#if defined(OS_MACOSX)
if (shutdown && getuid() == 0) {
shutdown = false;
}
#endif
#if defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
if (curtain_required)
return false;
#endif
if (shutdown) {
LOG(ERROR) << "The host username does not match.";
ShutdownHost(kUsernameMismatchExitCode);
}
} else {
HOST_LOG << "Policy does not require host username match.";
}
return false;
}
bool HostProcess::OnNatPolicyUpdate(bool nat_traversal_enabled) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (allow_nat_traversal_ != nat_traversal_enabled) {
if (nat_traversal_enabled)
HOST_LOG << "Policy enables NAT traversal.";
else
HOST_LOG << "Policy disables NAT traversal.";
allow_nat_traversal_ = nat_traversal_enabled;
return true;
}
return false;
}
void HostProcess::OnCurtainPolicyUpdate(bool curtain_required) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
#if defined(OS_MACOSX)
if (curtain_required) {
if (getuid() == 0) {
LOG(ERROR) << "Running the host in the console login session is yet not "
"supported.";
ShutdownHost(kLoginScreenNotSupportedExitCode);
return;
}
}
#endif
if (curtain_required_ != curtain_required) {
if (curtain_required)
HOST_LOG << "Policy requires curtain-mode.";
else
HOST_LOG << "Policy does not require curtain-mode.";
curtain_required_ = curtain_required;
if (host_)
host_->SetEnableCurtaining(curtain_required_);
}
}
bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
const std::string& talkgadget_prefix) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (talkgadget_prefix != talkgadget_prefix_) {
HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix;
talkgadget_prefix_ = talkgadget_prefix;
return true;
}
return false;
}
bool HostProcess::OnHostTokenUrlPolicyUpdate(
const GURL& token_url,
const GURL& token_validation_url,
const std::string& token_validation_cert_issuer) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (third_party_auth_config_.token_url != token_url ||
third_party_auth_config_.token_validation_url != token_validation_url ||
third_party_auth_config_.token_validation_cert_issuer !=
token_validation_cert_issuer) {
HOST_LOG << "Policy sets third-party token URLs: "
<< "TokenUrl: " << token_url << ", "
<< "TokenValidationUrl: " << token_validation_url
<< "TokenValidationCertificateIssuer: "
<< token_validation_cert_issuer;
third_party_auth_config_.token_url = token_url;
third_party_auth_config_.token_validation_url = token_validation_url;
third_party_auth_config_.token_validation_cert_issuer =
token_validation_cert_issuer;
return true;
}
return false;
}
bool HostProcess::OnPairingPolicyUpdate(bool allow_pairing) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (allow_pairing_ == allow_pairing)
return false;
if (allow_pairing)
HOST_LOG << "Policy enables client pairing.";
else
HOST_LOG << "Policy disables client pairing.";
allow_pairing_ = allow_pairing;
return true;
}
bool HostProcess::OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (enable_gnubby_auth_ == enable_gnubby_auth)
return false;
if (enable_gnubby_auth) {
HOST_LOG << "Policy enables gnubby auth.";
} else {
HOST_LOG << "Policy disables gnubby auth.";
}
enable_gnubby_auth_ = enable_gnubby_auth;
if (desktop_environment_factory_)
desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth);
return true;
}
void HostProcess::StartHost() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(!host_);
DCHECK(!signal_strategy_.get());
DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART ||
state_ == HOST_STOPPED) << state_;
state_ = HOST_STARTED;
signal_strategy_.reset(
new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
context_->url_request_context_getter(),
xmpp_server_config_));
scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
new DnsBlackholeChecker(context_->url_request_context_getter(),
talkgadget_prefix_));
network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
signaling_connector_.reset(new SignalingConnector(
signal_strategy_.get(),
dns_blackhole_checker.Pass(),
base::Bind(&HostProcess::OnAuthFailed, this)));
if (!oauth_refresh_token_.empty()) {
scoped_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials;
oauth_credentials.reset(
new OAuthTokenGetter::OAuthCredentials(
xmpp_server_config_.username, oauth_refresh_token_,
use_service_account_));
oauth_token_getter_.reset(new OAuthTokenGetter(
oauth_credentials.Pass(), context_->url_request_context_getter()));
signaling_connector_->EnableOAuth(oauth_token_getter_.get());
}
NetworkSettings network_settings(
allow_nat_traversal_ ?
NetworkSettings::NAT_TRAVERSAL_ENABLED :
NetworkSettings::NAT_TRAVERSAL_DISABLED);
if (!allow_nat_traversal_) {
network_settings.min_port = NetworkSettings::kDefaultMinPort;
network_settings.max_port = NetworkSettings::kDefaultMaxPort;
}
host_.reset(new ChromotingHost(
signal_strategy_.get(),
desktop_environment_factory_.get(),
CreateHostSessionManager(signal_strategy_.get(), network_settings,
context_->url_request_context_getter()),
context_->audio_task_runner(),
context_->input_task_runner(),
context_->video_capture_task_runner(),
context_->video_encode_task_runner(),
context_->network_task_runner(),
context_->ui_task_runner()));
#if defined(OS_LINUX)
host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
#endif
heartbeat_sender_.reset(new HeartbeatSender(
this, host_id_, signal_strategy_.get(), key_pair_,
directory_bot_jid_));
host_status_sender_.reset(new HostStatusSender(
host_id_, signal_strategy_.get(), key_pair_, directory_bot_jid_));
host_change_notification_listener_.reset(new HostChangeNotificationListener(
this, host_id_, signal_strategy_.get(), directory_bot_jid_));
log_to_server_.reset(
new LogToServer(host_->AsWeakPtr(), ServerLogEntry::ME2ME,
signal_strategy_.get(), directory_bot_jid_));
#if defined(REMOTING_MULTI_PROCESS)
host_event_logger_.reset(
new IpcHostEventLogger(host_->AsWeakPtr(), daemon_channel_.get()));
#else
host_event_logger_ =
HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
#endif
host_->SetEnableCurtaining(curtain_required_);
host_->Start(host_owner_);
CreateAuthenticatorFactory();
}
void HostProcess::OnAuthFailed() {
ShutdownHost(kInvalidOauthCredentialsExitCode);
}
void HostProcess::RestartHost() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK_EQ(state_, HOST_STARTED);
state_ = HOST_STOPPING_TO_RESTART;
ShutdownOnNetworkThread();
}
void HostProcess::ShutdownHost(HostExitCodes exit_code) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
*exit_code_out_ = exit_code;
switch (state_) {
case HOST_INITIALIZING:
state_ = HOST_STOPPING;
ShutdownOnNetworkThread();
break;
case HOST_STARTED:
state_ = HOST_STOPPING;
host_status_sender_->SendOfflineStatus(exit_code);
ScheduleHostShutdown();
break;
case HOST_STOPPING_TO_RESTART:
state_ = HOST_STOPPING;
break;
case HOST_STOPPING:
case HOST_STOPPED:
break;
}
}
void HostProcess::ScheduleHostShutdown() {
context_->network_task_runner()->PostDelayedTask(
FROM_HERE,
base::Bind(&HostProcess::ShutdownOnNetworkThread, base::Unretained(this)),
base::TimeDelta::FromSeconds(2));
}
void HostProcess::ShutdownOnNetworkThread() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
host_.reset();
host_event_logger_.reset();
log_to_server_.reset();
heartbeat_sender_.reset();
host_status_sender_.reset();
host_change_notification_listener_.reset();
signaling_connector_.reset();
oauth_token_getter_.reset();
signal_strategy_.reset();
network_change_notifier_.reset();
if (state_ == HOST_STOPPING_TO_RESTART) {
StartHost();
} else if (state_ == HOST_STOPPING) {
state_ = HOST_STOPPED;
if (policy_watcher_.get()) {
base::WaitableEvent done_event(true, false);
policy_watcher_->StopWatching(&done_event);
done_event.Wait();
policy_watcher_.reset();
}
config_watcher_.reset();
context_->ui_task_runner()->PostTask(
FROM_HERE,
base::Bind(&HostProcess::ShutdownOnUiThread, this));
} else {
NOTREACHED();
}
}
void HostProcess::OnCrash(const std::string& function_name,
const std::string& file_name,
const int& line_number) {
char message[1024];
base::snprintf(message, sizeof(message),
"Requested by %s at %s, line %d.",
function_name.c_str(), file_name.c_str(), line_number);
base::debug::Alias(message);
CHECK(false) << message;
}
int HostProcessMain() {
#if defined(TOOLKIT_GTK)
gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
#endif
net::EnableSSLServerSockets();
media::InitializeCPUSpecificMediaFeatures();
base::MessageLoopForUI message_loop;
scoped_ptr<ChromotingHostContext> context =
ChromotingHostContext::Create(new AutoThreadTaskRunner(
message_loop.message_loop_proxy(), base::MessageLoop::QuitClosure()));
if (!context)
return kInitializationFailed;
int exit_code = kSuccessExitCode;
new HostProcess(context.Pass(), &exit_code);
message_loop.Run();
return exit_code;
}
}
#if !defined(OS_WIN)
int main(int argc, char** argv) {
return remoting::HostMain(argc, argv);
}
#endif