Commit 10cd268f authored by rkjnsn's avatar rkjnsn Committed by Commit bot

Add support for multiple allowed domains

Adds two new policies, RemoteAccessHostDomainList and
RemoteAccessHostClientDomainList, which allow specifying a list of
allowed domains. Deprecates the existing RemoteAccessHostDomain and RemoteAccessHostClientDomain policies.

BUG=624620

Review-Url: https://codereview.chromium.org/2682473003
Cr-Commit-Position: refs/heads/master@{#468149}
parent 7086194f
......@@ -221,6 +221,9 @@
"RemoteAccessHostClientDomain": {
},
"RemoteAccessHostClientDomainList": {
},
"RemoteAccessHostFirewallTraversal": {
},
......@@ -230,6 +233,9 @@
"RemoteAccessHostDomain": {
},
"RemoteAccessHostDomainList": {
},
"RemoteAccessHostTalkGadgetPrefix": {
},
......
......@@ -143,7 +143,7 @@
# persistent IDs for all fields (but not for groups!) are needed. These are
# specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs,
# because doing so would break the deployed wire format!
# For your editing convenience: highest ID currently used: 367
# For your editing convenience: highest ID currently used: 369
# And don't forget to also update the EnterprisePolicies enum of
# histograms.xml (run 'python tools/metrics/histograms/update_policies.py').
#
......@@ -770,17 +770,38 @@
'dynamic_refresh': True,
'per_profile': False,
},
'deprecated': True,
'example_value': 'my-awesome-domain.com',
'id': 316,
'caption': '''Configure the required domain name for remote access clients''',
'tags': [],
'desc': '''Configures the required client domain name that will be imposed on remote access clients and prevents users from changing it.
'desc': '''This policy is deprecated. Please use RemoteAccessHostClientDomainList instead.''',
},
{
'name': 'RemoteAccessHostClientDomainList',
'type': 'list',
'schema': {
'type': 'array',
'items': { 'type': 'string' },
},
'supported_on': ['chrome.*:60-', 'chrome_os:60-'],
'features': {
'dynamic_refresh': True,
'per_profile': False,
},
'example_value': ['my-awesome-domain.com', 'my-auxiliary-domain.com'],
'id': 369,
'caption': '''Configure the required domain names for remote access clients''',
'tags': [],
'desc': '''Configures the required client domain names that will be imposed on remote access clients and prevents users from changing it.
If this setting is enabled, then only clients from one of the specified domains can connect to the host.
If this setting is enabled, then only clients from the specified domain can connect to the host.
If this setting is disabled or not set, then the default policy for the connection type is applied. For remote assistance, this allows clients from any domain to connect to the host; for anytime remote access, only the host owner can connect.
If this setting is disabled or not set, then the default policy for the connection type is applied. For remote assistance, this allows clients from any domain can connect to the host; for anytime remote access, only the host owner can connect.
This setting will override RemoteAccessHostClientDomain, if present.
See also RemoteAccessHostDomain.''',
See also RemoteAccessHostDomainList.''',
},
{
'name': 'RemoteAccessHostFirewallTraversal',
......@@ -812,17 +833,38 @@
'dynamic_refresh': True,
'per_profile': False,
},
'deprecated': True,
'example_value': 'my-awesome-domain.com',
'id': 154,
'caption': '''Configure the required domain name for remote access hosts''',
'tags': [],
'desc': '''Configures the required host domain name that will be imposed on remote access hosts and prevents users from changing it.
'desc': '''This policy is deprecated. Please use RemoteAccessHostDomainList instead.''',
},
{
'name': 'RemoteAccessHostDomainList',
'type': 'list',
'schema': {
'type': 'array',
'items': {'type': 'string' },
},
'supported_on': ['chrome.*:60-', 'chrome_os:60-'],
'features': {
'dynamic_refresh': True,
'per_profile': False,
},
'example_value': ['my-awesome-domain.com', 'my-auxiliary-domain.com'],
'id': 368,
'caption': '''Configure the required domain names for remote access hosts''',
'tags': [],
'desc': '''Configures the required host domain names that will be imposed on remote access hosts and prevents users from changing it.
If this setting is enabled, then hosts can be shared only using accounts registered on the specified domain name.
If this setting is enabled, then hosts can be shared only using accounts registered on one of the specified domain names.
If this setting is disabled or not set, then hosts can be shared using any account.
See also RemoteAccessHostClientDomain.''',
This setting will override RemoteAccessHostDomain, if present.
See also RemoteAccessHostClientDomainList.''',
},
{
'name': 'RemoteAccessHostRequireTwoFactor',
......
......@@ -174,11 +174,19 @@ void It2MeHost::FinishConnect() {
}
// Check the host domain policy.
if (!required_host_domain_.empty() &&
!base::EndsWith(username_, std::string("@") + required_host_domain_,
base::CompareCase::INSENSITIVE_ASCII)) {
SetState(kInvalidDomainError, "");
return;
if (!required_host_domain_list_.empty()) {
bool matched = false;
for (const auto& domain : required_host_domain_list_) {
if (base::EndsWith(username_, std::string("@") + domain,
base::CompareCase::INSENSITIVE_ASCII)) {
matched = true;
break;
}
}
if (!matched) {
SetState(kInvalidDomainError, "");
return;
}
}
// Generate a key pair for the Host to use.
......@@ -314,14 +322,23 @@ void It2MeHost::OnPolicyUpdate(
&nat_policy)) {
UpdateNatPolicy(nat_policy);
}
std::string host_domain;
if (policies->GetString(policy::key::kRemoteAccessHostDomain, &host_domain)) {
UpdateHostDomainPolicy(host_domain);
const base::ListValue* host_domain_list;
if (policies->GetList(policy::key::kRemoteAccessHostDomainList,
&host_domain_list)) {
std::vector<std::string> host_domain_list_vector;
for (const auto& value : *host_domain_list) {
host_domain_list_vector.push_back(value.GetString());
}
UpdateHostDomainListPolicy(std::move(host_domain_list_vector));
}
std::string client_domain;
if (policies->GetString(policy::key::kRemoteAccessHostClientDomain,
&client_domain)) {
UpdateClientDomainPolicy(client_domain);
const base::ListValue* client_domain_list;
if (policies->GetList(policy::key::kRemoteAccessHostClientDomainList,
&client_domain_list)) {
std::vector<std::string> client_domain_list_vector;
for (const auto& value : *client_domain_list) {
client_domain_list_vector.push_back(value.GetString());
}
UpdateClientDomainListPolicy(std::move(client_domain_list_vector));
}
policy_received_ = true;
......@@ -355,30 +372,34 @@ void It2MeHost::UpdateNatPolicy(bool nat_traversal_enabled) {
nat_traversal_enabled_));
}
void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
void It2MeHost::UpdateHostDomainListPolicy(
std::vector<std::string> host_domain_list) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
VLOG(2) << "UpdateHostDomainPolicy: " << host_domain;
VLOG(2) << "UpdateHostDomainListPolicy: "
<< base::JoinString(host_domain_list, ", ");
// When setting a host domain policy, force disconnect any existing session.
if (!host_domain.empty() && IsRunning()) {
if (!host_domain_list.empty() && IsRunning()) {
DisconnectOnNetworkThread();
}
required_host_domain_ = host_domain;
required_host_domain_list_ = std::move(host_domain_list);
}
void It2MeHost::UpdateClientDomainPolicy(const std::string& client_domain) {
void It2MeHost::UpdateClientDomainListPolicy(
std::vector<std::string> client_domain_list) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
VLOG(2) << "UpdateClientDomainPolicy: " << client_domain;
VLOG(2) << "UpdateClientDomainPolicy: "
<< base::JoinString(client_domain_list, ", ");
// When setting a client domain policy, disconnect any existing session.
if (!client_domain.empty() && IsRunning()) {
if (!client_domain_list.empty() && IsRunning()) {
DisconnectOnNetworkThread();
}
required_client_domain_ = client_domain;
required_client_domain_list_ = std::move(client_domain_list);
}
void It2MeHost::SetState(It2MeHostState state,
......@@ -502,12 +523,18 @@ void It2MeHost::ValidateConnectionDetails(
}
// Check the client domain policy.
if (!required_client_domain_.empty()) {
if (!base::EndsWith(client_username,
std::string("@") + required_client_domain_,
base::CompareCase::INSENSITIVE_ASCII)) {
if (!required_client_domain_list_.empty()) {
bool matched = false;
for (const auto& domain : required_client_domain_list_) {
if (base::EndsWith(client_username, std::string("@") + domain,
base::CompareCase::INSENSITIVE_ASCII)) {
matched = true;
break;
}
}
if (!matched) {
LOG(ERROR) << "Rejecting incoming connection from " << remote_jid
<< ": Domain mismatch.";
<< ": Domain not allowed.";
result_callback.Run(ValidationResult::ERROR_INVALID_ACCOUNT);
DisconnectOnNetworkThread();
return;
......
......@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
......@@ -137,8 +138,9 @@ class It2MeHost : public base::RefCountedThreadSafe<It2MeHost>,
// Handlers for NAT traversal and domain policies.
void UpdateNatPolicy(bool nat_traversal_enabled);
void UpdateHostDomainPolicy(const std::string& host_domain);
void UpdateClientDomainPolicy(const std::string& client_domain);
void UpdateHostDomainListPolicy(std::vector<std::string> host_domain_list);
void UpdateClientDomainListPolicy(
std::vector<std::string> client_domain_list);
void DisconnectOnNetworkThread();
......@@ -174,8 +176,8 @@ class It2MeHost : public base::RefCountedThreadSafe<It2MeHost>,
bool nat_traversal_enabled_ = false;
// The client and host domain policy setting.
std::string required_client_domain_;
std::string required_host_domain_;
std::vector<std::string> required_client_domain_list_;
std::vector<std::string> required_host_domain_list_;
// Tracks the JID of the remote user when in a connecting state.
std::string connecting_jid_;
......
......@@ -354,6 +354,80 @@ TEST_F(It2MeHostTest, HostValidation_HostDomainPolicy_MatchEnd) {
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainListPolicy_MatchFirst) {
base::ListValue domains;
domains.AppendString(kMatchingDomain);
domains.AppendString(kMismatchedDomain1);
SetPolicies({{policy::key::kRemoteAccessHostDomainList, domains}});
StartHost();
ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainListPolicy_MatchSecond) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMatchingDomain);
SetPolicies({{policy::key::kRemoteAccessHostDomainList, domains}});
StartHost();
ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainListPolicy_NoMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMismatchedDomain2);
SetPolicies({{policy::key::kRemoteAccessHostDomainList, domains}});
StartHost();
ASSERT_EQ(It2MeHostState::kInvalidDomainError, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainBothPolicies_BothMatch) {
base::ListValue domains;
domains.AppendString(kMatchingDomain);
domains.AppendString(kMismatchedDomain1);
SetPolicies(
{{policy::key::kRemoteAccessHostDomain, base::Value(kMatchingDomain)},
{policy::key::kRemoteAccessHostDomainList, domains}});
StartHost();
ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainBothPolicies_ListMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMatchingDomain);
SetPolicies(
{{policy::key::kRemoteAccessHostDomain, base::Value(kMismatchedDomain1)},
{policy::key::kRemoteAccessHostDomainList, domains}});
// Should succeed even though the legacy policy would deny.
StartHost();
ASSERT_EQ(It2MeHostState::kReceivedAccessCode, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, HostValidation_HostDomainBothPolicies_LegacyMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMismatchedDomain2);
SetPolicies(
{{policy::key::kRemoteAccessHostDomain, base::Value(kMatchingDomain)},
{policy::key::kRemoteAccessHostDomainList, domains}});
// Should fail even though the legacy policy would allow.
StartHost();
ASSERT_EQ(It2MeHostState::kInvalidDomainError, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_NoClientDomainPolicy_ValidJid) {
StartHost();
RunValidationCallback(kTestClientJid);
......@@ -451,6 +525,91 @@ TEST_F(It2MeHostTest, ConnectionValidation_WrongClientDomain_MatchEnd) {
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ClientDomainListPolicy_MatchFirst) {
base::ListValue domains;
domains.AppendString(kMatchingDomain);
domains.AppendString(kMismatchedDomain1);
SetPolicies({{policy::key::kRemoteAccessHostClientDomainList, domains}});
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::SUCCESS, validation_result_);
ASSERT_EQ(It2MeHostState::kConnecting, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ClientDomainListPolicy_MatchSecond) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMatchingDomain);
SetPolicies({{policy::key::kRemoteAccessHostClientDomainList, domains}});
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::SUCCESS, validation_result_);
ASSERT_EQ(It2MeHostState::kConnecting, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ClientDomainListPolicy_NoMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMismatchedDomain2);
SetPolicies({{policy::key::kRemoteAccessHostClientDomainList, domains}});
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::ERROR_INVALID_ACCOUNT, validation_result_);
RunUntilStateChanged(It2MeHostState::kDisconnected);
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ClientDomainBothPolicies_BothMatch) {
base::ListValue domains;
domains.AppendString(kMatchingDomain);
domains.AppendString(kMismatchedDomain1);
SetPolicies({{policy::key::kRemoteAccessHostClientDomain,
base::Value(kMatchingDomain)},
{policy::key::kRemoteAccessHostClientDomainList, domains}});
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::SUCCESS, validation_result_);
ASSERT_EQ(It2MeHostState::kConnecting, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ClientDomainBothPolicies_ListMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMatchingDomain);
SetPolicies({{policy::key::kRemoteAccessHostClientDomain,
base::Value(kMismatchedDomain1)},
{policy::key::kRemoteAccessHostClientDomainList, domains}});
// Should succeed even though the legacy policy would deny.
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::SUCCESS, validation_result_);
ASSERT_EQ(It2MeHostState::kConnecting, last_host_state_);
ShutdownHost();
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest,
ConnectionValidation_ClientDomainBothPolicies_LegacyMatch) {
base::ListValue domains;
domains.AppendString(kMismatchedDomain1);
domains.AppendString(kMismatchedDomain2);
SetPolicies({{policy::key::kRemoteAccessHostClientDomain,
base::Value(kMatchingDomain)},
{policy::key::kRemoteAccessHostClientDomainList, domains}});
// Should fail even though the legacy policy would allow.
StartHost();
RunValidationCallback(kTestClientJid);
ASSERT_EQ(ValidationResult::ERROR_INVALID_ACCOUNT, validation_result_);
RunUntilStateChanged(It2MeHostState::kDisconnected);
ASSERT_EQ(It2MeHostState::kDisconnected, last_host_state_);
}
TEST_F(It2MeHostTest, ConnectionValidation_ConfirmationDialog_Accept) {
StartHost();
RunValidationCallback(kTestClientJid);
......
......@@ -181,8 +181,10 @@ PolicyWatcher::PolicyWatcher(
default_values_->SetBoolean(key::kRemoteAccessHostFirewallTraversal, true);
default_values_->SetBoolean(key::kRemoteAccessHostRequireCurtain, false);
default_values_->SetBoolean(key::kRemoteAccessHostMatchUsername, false);
default_values_->SetString(key::kRemoteAccessHostClientDomain, std::string());
default_values_->SetString(key::kRemoteAccessHostDomain, std::string());
default_values_->Set(key::kRemoteAccessHostClientDomainList,
base::MakeUnique<base::ListValue>());
default_values_->Set(key::kRemoteAccessHostDomainList,
base::MakeUnique<base::ListValue>());
default_values_->SetString(key::kRemoteAccessHostTalkGadgetPrefix,
kDefaultHostTalkGadgetPrefix);
default_values_->SetString(key::kRemoteAccessHostTokenUrl, std::string());
......@@ -232,6 +234,7 @@ bool PolicyWatcher::NormalizePolicies(base::DictionaryValue* policy_dict) {
LOG(WARNING) << "Unknown (unrecognized or unsupported) policy: " << path
<< ": " << error;
}
HandleDeprecatedPolicies(policy_dict);
return true;
} else {
LOG(ERROR) << "Invalid policy contents: " << path << ": " << error;
......@@ -239,6 +242,37 @@ bool PolicyWatcher::NormalizePolicies(base::DictionaryValue* policy_dict) {
}
}
void PolicyWatcher::HandleDeprecatedPolicies(base::DictionaryValue* dict) {
// RemoteAccessHostDomain
if (dict->HasKey(policy::key::kRemoteAccessHostDomain)) {
if (!dict->HasKey(policy::key::kRemoteAccessHostDomainList)) {
std::string domain;
dict->GetString(policy::key::kRemoteAccessHostDomain, &domain);
if (!domain.empty()) {
auto list = base::MakeUnique<base::ListValue>();
list->AppendString(domain);
dict->Set(policy::key::kRemoteAccessHostDomainList, std::move(list));
}
}
dict->Remove(policy::key::kRemoteAccessHostDomain, nullptr);
}
// RemoteAccessHostClientDomain
if (dict->HasKey(policy::key::kRemoteAccessHostClientDomain)) {
if (!dict->HasKey(policy::key::kRemoteAccessHostClientDomainList)) {
std::string domain;
dict->GetString(policy::key::kRemoteAccessHostClientDomain, &domain);
if (!domain.empty()) {
auto list = base::MakeUnique<base::ListValue>();
list->AppendString(domain);
dict->Set(policy::key::kRemoteAccessHostClientDomainList,
std::move(list));
}
}
dict->Remove(policy::key::kRemoteAccessHostClientDomain, nullptr);
}
}
namespace {
void CopyDictionaryValue(const base::DictionaryValue& from,
base::DictionaryValue& to,
......
......@@ -92,12 +92,18 @@ class PolicyWatcher : public policy::PolicyService::Observer,
// Gets Chromoting schema stored inside |owned_schema_registry_|.
const policy::Schema* GetPolicySchema() const;
// Simplifying wrapper around Schema::Normalize.
// Normalizes policies using Schema::Normalize and converts deprecated
// policies.
//
// - Returns false if |dict| is invalid (i.e. contains mistyped policy
// values).
// - Returns true if |dict| was valid or got normalized.
bool NormalizePolicies(base::DictionaryValue* dict);
// Converts each deprecated policy to its replacement if and only if the
// replacement policy is not set, and removes deprecated policied from dict.
void HandleDeprecatedPolicies(base::DictionaryValue* dict);
// Stores |new_policies| into |old_policies_|. Returns dictionary with items
// from |new_policies| that are different from the old |old_policies_|.
std::unique_ptr<base::DictionaryValue> StoreNewAndReturnChangedPolicies(
......
This diff is collapsed.
......@@ -10,6 +10,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
......@@ -294,10 +295,10 @@ class HostProcess : public ConfigWatcher::Delegate,
void OnPolicyUpdate(std::unique_ptr<base::DictionaryValue> policies);
void OnPolicyError();
void ReportPolicyErrorAndRestartHost();
void ApplyHostDomainPolicy();
void ApplyHostDomainListPolicy();
void ApplyUsernamePolicy();
bool OnClientDomainPolicyUpdate(base::DictionaryValue* policies);
bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies);
bool OnClientDomainListPolicyUpdate(base::DictionaryValue* policies);
bool OnHostDomainListPolicyUpdate(base::DictionaryValue* policies);
bool OnUsernamePolicyUpdate(base::DictionaryValue* policies);
bool OnNatPolicyUpdate(base::DictionaryValue* policies);
bool OnRelayPolicyUpdate(base::DictionaryValue* policies);
......@@ -375,8 +376,8 @@ class HostProcess : public ConfigWatcher::Delegate,
std::unique_ptr<PolicyWatcher> policy_watcher_;
PolicyState policy_state_ = POLICY_INITIALIZING;
std::string client_domain_;
std::string host_domain_;
std::vector<std::string> client_domain_list_;
std::vector<std::string> host_domain_list_;
bool host_username_match_required_ = false;
bool allow_nat_traversal_ = true;
bool allow_relay_ = true;
......@@ -591,7 +592,7 @@ void HostProcess::OnConfigUpdated(
} else if (state_ == HOST_STARTED) {
// Reapply policies that could be affected by a new config.
DCHECK_EQ(policy_state_, POLICY_LOADED);
ApplyHostDomainPolicy();
ApplyHostDomainListPolicy();
ApplyUsernamePolicy();
// TODO(sergeyu): Here we assume that PIN is the only part of the config
......@@ -724,7 +725,7 @@ void HostProcess::CreateAuthenticatorFactory() {
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithPin(
use_service_account_, host_owner_, local_certificate, key_pair_,
client_domain_, pin_hash_, pairing_registry);
client_domain_list_, pin_hash_, pairing_registry);
host_->set_pairing_registry(pairing_registry);
} else {
......@@ -748,7 +749,7 @@ void HostProcess::CreateAuthenticatorFactory() {
context_->url_request_context_getter());
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
use_service_account_, host_owner_, local_certificate, key_pair_,
client_domain_, token_validator_factory);
client_domain_list_, token_validator_factory);
}
#if defined(OS_POSIX)
......@@ -1019,8 +1020,8 @@ void HostProcess::OnPolicyUpdate(
}
bool restart_required = false;
restart_required |= OnClientDomainPolicyUpdate(policies.get());
restart_required |= OnHostDomainPolicyUpdate(policies.get());
restart_required |= OnClientDomainListPolicyUpdate(policies.get());
restart_required |= OnHostDomainListPolicyUpdate(policies.get());
restart_required |= OnCurtainPolicyUpdate(policies.get());
// Note: UsernamePolicyUpdate must run after OnCurtainPolicyUpdate.
restart_required |= OnUsernamePolicyUpdate(policies.get());
......@@ -1069,13 +1070,14 @@ void HostProcess::ReportPolicyErrorAndRestartHost() {
RestartHost(kHostOfflineReasonPolicyReadError);
}
void HostProcess::ApplyHostDomainPolicy() {
void HostProcess::ApplyHostDomainListPolicy() {
if (state_ != HOST_STARTED)
return;
HOST_LOG << "Policy sets host domain: " << host_domain_;
HOST_LOG << "Policy sets host domains: "
<< base::JoinString(host_domain_list_, ", ");
if (!host_domain_.empty()) {
if (!host_domain_list_.empty()) {
// If the user does not have a Google email, their client JID will not be
// based on their email. In that case, the username/host domain policies
// would be meaningless, since there is no way to check that the JID
......@@ -1086,32 +1088,55 @@ void HostProcess::ApplyHostDomainPolicy() {
ShutdownHost(kInvalidHostDomainExitCode);
}
if (!base::EndsWith(host_owner_, std::string("@") + host_domain_,
base::CompareCase::INSENSITIVE_ASCII)) {
bool matched = false;
for (const std::string& domain : host_domain_list_) {
if (base::EndsWith(host_owner_, std::string("@") + domain,
base::CompareCase::INSENSITIVE_ASCII)) {
matched = true;
}
}
if (!matched) {
LOG(ERROR) << "The host domain does not match the policy.";
ShutdownHost(kInvalidHostDomainExitCode);
}
}
}
bool HostProcess::OnHostDomainPolicyUpdate(base::DictionaryValue* policies) {
bool HostProcess::OnHostDomainListPolicyUpdate(
base::DictionaryValue* policies) {
// Returns true if the host has to be restarted after this policy update.
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
if (!policies->GetString(policy::key::kRemoteAccessHostDomain,
&host_domain_)) {
const base::ListValue* list;
if (!policies->GetList(policy::key::kRemoteAccessHostDomainList, &list)) {
return false;
}
ApplyHostDomainPolicy();
host_domain_list_.clear();
for (const auto& value : *list) {
host_domain_list_.push_back(value.GetString());
}
ApplyHostDomainListPolicy();
return false;
}