Commit b438fb9d authored by jamiewalch's avatar jamiewalch Committed by Commit bot

Add policy to restrict client domain for Me2Me.

BUG=b/19316131

Review URL: https://codereview.chromium.org/1643793002

Cr-Commit-Position: refs/heads/master@{#374209}
parent eb17ae16
......@@ -204,6 +204,9 @@
"RemoteAccessClientFirewallTraversal": {
},
"RemoteAccessHostClientDomain": {
},
"RemoteAccessHostFirewallTraversal": {
},
......
......@@ -137,7 +137,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: 315
# For your editing convenience: highest ID currently used: 316
#
# Placeholders:
# The following placeholder strings are automatically substituted:
......@@ -700,6 +700,27 @@
If this setting is disabled and outgoing UDP connections are filtered by the firewall, then this machine can only connect to host machines within the local network.''',
},
{
'name': 'RemoteAccessHostClientDomain',
'type': 'string',
'schema': { 'type': 'string' },
'supported_on': ['chrome.*:22-', 'chrome_os:41-'],
'features': {
'dynamic_refresh': True,
'per_profile': False,
},
'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.
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 can connect to the host; for anytime remote access, only the host owner can connect.
See also RemoteAccessHostDomain.''',
},
{
'name': 'RemoteAccessHostFirewallTraversal',
'type': 'main',
......@@ -738,7 +759,9 @@
If this setting is enabled, then hosts can be shared only using accounts registered on the specified domain name.
If this setting is disabled or not set, then hosts can be shared using any account.''',
If this setting is disabled or not set, then hosts can be shared using any account.
See also RemoteAccessHostClientDomain.''',
},
{
'name': 'RemoteAccessHostRequireTwoFactor',
......
......@@ -332,6 +332,11 @@ void It2MeHost::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
if (policies->GetString(policy::key::kRemoteAccessHostDomain, &host_domain)) {
UpdateHostDomainPolicy(host_domain);
}
std::string client_domain;
if (policies->GetString(policy::key::kRemoteAccessHostClientDomain,
&client_domain)) {
UpdateClientDomainPolicy(client_domain);
}
policy_received_ = true;
......@@ -377,6 +382,19 @@ void It2MeHost::UpdateHostDomainPolicy(const std::string& host_domain) {
required_host_domain_ = host_domain;
}
void It2MeHost::UpdateClientDomainPolicy(const std::string& client_domain) {
DCHECK(host_context_->network_task_runner()->BelongsToCurrentThread());
VLOG(2) << "UpdateClientDomainPolicy: " << client_domain;
// When setting a client domain policy, disconnect any existing session.
if (!client_domain.empty() && IsConnected()) {
Shutdown();
}
required_client_domain_ = client_domain;
}
It2MeHost::~It2MeHost() {
// Check that resources that need to be torn down on the UI thread are gone.
DCHECK(!desktop_environment_factory_.get());
......@@ -459,7 +477,8 @@ void It2MeHost::OnReceivedSupportID(
scoped_ptr<protocol::AuthenticatorFactory> factory(
new protocol::It2MeHostAuthenticatorFactory(
local_certificate, host_key_pair_, access_code));
local_certificate, host_key_pair_, access_code,
required_client_domain_));
host_->SetAuthenticatorFactory(std::move(factory));
// Pass the Access Code to the script object before changing state.
......
......@@ -133,9 +133,10 @@ class It2MeHost : public base::RefCountedThreadSafe<It2MeHost>,
// Called when malformed policies are detected.
void OnPolicyError();
// Handlers for NAT traversal and host domain policies.
// 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 Shutdown();
......@@ -165,7 +166,8 @@ class It2MeHost : public base::RefCountedThreadSafe<It2MeHost>,
// Host the current nat traversal policy setting.
bool nat_traversal_enabled_;
// The host domain policy setting.
// The client and host domain policy setting.
std::string required_client_domain_;
std::string required_host_domain_;
// Indicates whether or not a policy has ever been read. This is to ensure
......
......@@ -179,6 +179,7 @@ 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_->SetString(key::kRemoteAccessHostTalkGadgetPrefix,
kDefaultHostTalkGadgetPrefix);
......
......@@ -242,6 +242,7 @@ class PolicyWatcherTest : public testing::Test {
dict.SetBoolean(key::kRemoteAccessHostFirewallTraversal, true);
dict.SetBoolean(key::kRemoteAccessHostAllowRelayedConnection, true);
dict.SetString(key::kRemoteAccessHostUdpPortRange, "");
dict.SetString(key::kRemoteAccessHostClientDomain, std::string());
dict.SetString(key::kRemoteAccessHostDomain, std::string());
dict.SetBoolean(key::kRemoteAccessHostMatchUsername, false);
dict.SetString(key::kRemoteAccessHostTalkGadgetPrefix,
......
......@@ -338,6 +338,7 @@ class HostProcess : public ConfigWatcher::Delegate,
void ReportPolicyErrorAndRestartHost();
void ApplyHostDomainPolicy();
void ApplyUsernamePolicy();
bool OnClientDomainPolicyUpdate(base::DictionaryValue* policies);
bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies);
bool OnUsernamePolicyUpdate(base::DictionaryValue* policies);
bool OnNatPolicyUpdate(base::DictionaryValue* policies);
......@@ -412,6 +413,7 @@ class HostProcess : public ConfigWatcher::Delegate,
scoped_ptr<PolicyWatcher> policy_watcher_;
PolicyState policy_state_;
std::string client_domain_;
std::string host_domain_;
bool host_username_match_required_;
bool allow_nat_traversal_;
......@@ -794,7 +796,7 @@ void HostProcess::CreateAuthenticatorFactory() {
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
use_service_account_, host_owner_, local_certificate, key_pair_,
host_secret_hash_, pairing_registry);
client_domain_, host_secret_hash_, pairing_registry);
host_->set_pairing_registry(pairing_registry);
} else {
......@@ -807,7 +809,7 @@ void HostProcess::CreateAuthenticatorFactory() {
key_pair_, context_->url_request_context_getter()));
factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
use_service_account_, host_owner_, local_certificate, key_pair_,
std::move(token_validator_factory));
client_domain_, std::move(token_validator_factory));
}
#if defined(OS_POSIX)
......@@ -1104,6 +1106,7 @@ void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
}
bool restart_required = false;
restart_required |= OnClientDomainPolicyUpdate(policies.get());
restart_required |= OnHostDomainPolicyUpdate(policies.get());
restart_required |= OnCurtainPolicyUpdate(policies.get());
// Note: UsernamePolicyUpdate must run after OnCurtainPolicyUpdate.
......@@ -1191,6 +1194,13 @@ bool HostProcess::OnHostDomainPolicyUpdate(base::DictionaryValue* policies) {
return false;
}
bool HostProcess::OnClientDomainPolicyUpdate(base::DictionaryValue* policies) {
// Returns true if the host has to be restarted after this policy update.
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
return policies->GetString(policy::key::kRemoteAccessHostClientDomain,
&client_domain_);
}
void HostProcess::ApplyUsernamePolicy() {
if (state_ != HOST_STARTED)
return;
......
......@@ -5,8 +5,10 @@
#include "remoting/protocol/it2me_host_authenticator_factory.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/protocol/negotiating_host_authenticator.h"
#include "remoting/protocol/rejecting_authenticator.h"
namespace remoting {
namespace protocol {
......@@ -14,10 +16,12 @@ namespace protocol {
It2MeHostAuthenticatorFactory::It2MeHostAuthenticatorFactory(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& shared_secret)
const std::string& shared_secret,
const std::string& required_client_domain)
: local_cert_(local_cert),
key_pair_(key_pair),
shared_secret_(shared_secret) {
shared_secret_(shared_secret),
required_client_domain_(required_client_domain) {
}
It2MeHostAuthenticatorFactory::~It2MeHostAuthenticatorFactory() {
......@@ -27,6 +31,23 @@ scoped_ptr<Authenticator> It2MeHostAuthenticatorFactory::CreateAuthenticator(
const std::string& local_jid,
const std::string& remote_jid,
const buzz::XmlElement* first_message) {
// Check the client domain policy.
if (!required_client_domain_.empty()) {
std::string client_username = remote_jid;
size_t pos = client_username.find('/');
if (pos != std::string::npos) {
client_username.replace(pos, std::string::npos, "");
}
if (!base::EndsWith(client_username,
std::string("@") + required_client_domain_,
base::CompareCase::INSENSITIVE_ASCII)) {
LOG(ERROR) << "Rejecting incoming connection from " << remote_jid
<< ": Domain mismatch.";
return make_scoped_ptr(
new RejectingAuthenticator(Authenticator::INVALID_CREDENTIALS));
}
}
return NegotiatingHostAuthenticator::CreateWithSharedSecret(
local_cert_, key_pair_, shared_secret_, AuthenticationMethod::NONE,
nullptr);
......
......@@ -26,7 +26,8 @@ class It2MeHostAuthenticatorFactory : public AuthenticatorFactory {
It2MeHostAuthenticatorFactory(
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& shared_secret);
const std::string& shared_secret,
const std::string& required_client_domain);
~It2MeHostAuthenticatorFactory() override;
// AuthenticatorFactory interface.
......@@ -39,6 +40,7 @@ class It2MeHostAuthenticatorFactory : public AuthenticatorFactory {
std::string local_cert_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string shared_secret_;
std::string required_client_domain_;
DISALLOW_COPY_AND_ASSIGN(It2MeHostAuthenticatorFactory);
};
......
......@@ -11,6 +11,7 @@
#include "remoting/base/rsa_key_pair.h"
#include "remoting/protocol/channel_authenticator.h"
#include "remoting/protocol/negotiating_host_authenticator.h"
#include "remoting/protocol/rejecting_authenticator.h"
#include "remoting/protocol/token_validator.h"
#include "remoting/signaling/jid_util.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
......@@ -18,54 +19,6 @@
namespace remoting {
namespace protocol {
namespace {
// Authenticator that accepts one message and rejects connection after that.
class RejectingAuthenticator : public Authenticator {
public:
RejectingAuthenticator()
: state_(WAITING_MESSAGE) {
}
~RejectingAuthenticator() override {}
State state() const override { return state_; }
bool started() const override { return true; }
RejectionReason rejection_reason() const override {
DCHECK_EQ(state_, REJECTED);
return INVALID_CREDENTIALS;
}
void ProcessMessage(const buzz::XmlElement* message,
const base::Closure& resume_callback) override {
DCHECK_EQ(state_, WAITING_MESSAGE);
state_ = REJECTED;
resume_callback.Run();
}
scoped_ptr<buzz::XmlElement> GetNextMessage() override {
NOTREACHED();
return nullptr;
}
const std::string& GetAuthKey() const override {
NOTREACHED();
return auth_key_;
};
scoped_ptr<ChannelAuthenticator> CreateChannelAuthenticator() const override {
NOTREACHED();
return nullptr;
}
protected:
State state_;
std::string auth_key_;
};
} // namespace
// static
scoped_ptr<AuthenticatorFactory>
Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
......@@ -73,6 +26,7 @@ Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
const SharedSecretHash& shared_secret_hash,
scoped_refptr<PairingRegistry> pairing_registry) {
scoped_ptr<Me2MeHostAuthenticatorFactory> result(
......@@ -81,6 +35,7 @@ Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
result->host_owner_ = host_owner;
result->local_cert_ = local_cert;
result->key_pair_ = key_pair;
result->required_client_domain_ = required_client_domain;
result->shared_secret_hash_ = shared_secret_hash;
result->pairing_registry_ = pairing_registry;
return std::move(result);
......@@ -94,6 +49,7 @@ Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
scoped_ptr<TokenValidatorFactory>
token_validator_factory) {
scoped_ptr<Me2MeHostAuthenticatorFactory> result(
......@@ -102,6 +58,7 @@ Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
result->host_owner_ = host_owner;
result->local_cert_ = local_cert;
result->key_pair_ = key_pair;
result->required_client_domain_ = required_client_domain;
result->token_validator_factory_ = std::move(token_validator_factory);
return std::move(result);
}
......@@ -126,7 +83,8 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
// account will have the same prefix.
if (!SplitJidResource(local_jid, &remote_jid_prefix, nullptr)) {
LOG(DFATAL) << "Invalid local JID:" << local_jid;
return make_scoped_ptr(new RejectingAuthenticator());
return make_scoped_ptr(
new RejectingAuthenticator(Authenticator::INVALID_CREDENTIALS));
}
} else {
// TODO(rmsousa): This only works for cases where the JID prefix matches
......@@ -139,8 +97,27 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
if (!base::IsStringASCII(remote_jid) ||
!base::StartsWith(remote_jid, remote_jid_prefix + '/',
base::CompareCase::INSENSITIVE_ASCII)) {
LOG(ERROR) << "Rejecting incoming connection from " << remote_jid;
return make_scoped_ptr(new RejectingAuthenticator());
LOG(ERROR) << "Rejecting incoming connection from " << remote_jid
<< ": Prefix mismatch.";
return make_scoped_ptr(
new RejectingAuthenticator(Authenticator::INVALID_CREDENTIALS));
}
// If necessary, verify that the client's jid belongs to the correct domain.
if (!required_client_domain_.empty()) {
std::string client_username = remote_jid;
size_t pos = client_username.find('/');
if (pos != std::string::npos) {
client_username.replace(pos, std::string::npos, "");
}
if (!base::EndsWith(client_username,
std::string("@") + required_client_domain_,
base::CompareCase::INSENSITIVE_ASCII)) {
LOG(ERROR) << "Rejecting incoming connection from " << remote_jid
<< ": Domain mismatch.";
return make_scoped_ptr(
new RejectingAuthenticator(Authenticator::INVALID_CREDENTIALS));
}
}
if (!local_cert_.empty() && key_pair_.get()) {
......@@ -156,7 +133,8 @@ scoped_ptr<Authenticator> Me2MeHostAuthenticatorFactory::CreateAuthenticator(
shared_secret_hash_.hash_function, pairing_registry_);
}
return make_scoped_ptr(new RejectingAuthenticator());
return make_scoped_ptr(
new RejectingAuthenticator(Authenticator::INVALID_CREDENTIALS));
}
} // namespace protocol
......
......@@ -32,6 +32,7 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
const SharedSecretHash& shared_secret_hash,
scoped_refptr<PairingRegistry> pairing_registry);
......@@ -41,6 +42,7 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
const std::string& host_owner,
const std::string& local_cert,
scoped_refptr<RsaKeyPair> key_pair,
const std::string& required_client_domain,
scoped_ptr<TokenValidatorFactory> token_validator_factory);
Me2MeHostAuthenticatorFactory();
......@@ -58,6 +60,7 @@ class Me2MeHostAuthenticatorFactory : public AuthenticatorFactory {
std::string host_owner_;
std::string local_cert_;
scoped_refptr<RsaKeyPair> key_pair_;
std::string required_client_domain_;
// Used only for shared secret host authenticators.
SharedSecretHash shared_secret_hash_;
......
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/protocol/rejecting_authenticator.h"
#include "base/logging.h"
#include "remoting/protocol/channel_authenticator.h"
#include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
namespace remoting {
namespace protocol {
RejectingAuthenticator::RejectingAuthenticator(RejectionReason rejection_reason)
: rejection_reason_(rejection_reason) {
}
RejectingAuthenticator::~RejectingAuthenticator() = default;
Authenticator::State RejectingAuthenticator::state() const {
return state_;
}
bool RejectingAuthenticator::started() const {
return true;
}
Authenticator::RejectionReason
RejectingAuthenticator::rejection_reason() const {
DCHECK_EQ(state_, REJECTED);
return rejection_reason_;
}
void RejectingAuthenticator::ProcessMessage(
const buzz::XmlElement* message,
const base::Closure& resume_callback) {
DCHECK_EQ(state_, WAITING_MESSAGE);
state_ = REJECTED;
resume_callback.Run();
}
scoped_ptr<buzz::XmlElement> RejectingAuthenticator::GetNextMessage() {
NOTREACHED();
return nullptr;
}
const std::string& RejectingAuthenticator::GetAuthKey() const {
NOTREACHED();
return auth_key_;
};
scoped_ptr<ChannelAuthenticator>
RejectingAuthenticator::CreateChannelAuthenticator() const {
NOTREACHED();
return nullptr;
}
} // namespace protocol
} // namespace remoting
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef REMOTING_PROTOCOL_REJECTING_AUTHENTICATOR_FACTORY_H_
#define REMOTING_PROTOCOL_REJECTING_AUTHENTICATOR_FACTORY_H_
#include <string>
#include "remoting/protocol/authenticator.h"
namespace remoting {
namespace protocol {
// Authenticator that accepts one message and rejects connection after that.
class RejectingAuthenticator : public Authenticator {
public:
RejectingAuthenticator(RejectionReason rejection_reason);
~RejectingAuthenticator() override;
// Authenticator interface
State state() const override;
bool started() const override;
RejectionReason rejection_reason() const override;
void ProcessMessage(const buzz::XmlElement* message,
const base::Closure& resume_callback) override;
scoped_ptr<buzz::XmlElement> GetNextMessage() override;
const std::string& GetAuthKey() const override;
scoped_ptr<ChannelAuthenticator> CreateChannelAuthenticator() const override;
private:
RejectionReason rejection_reason_;
State state_ = WAITING_MESSAGE;
std::string auth_key_;
DISALLOW_COPY_AND_ASSIGN(RejectingAuthenticator);
};
} // namespace protocol
} // namespace remoting
#endif // REMOTING_PROTOCOL_REJECTING_AUTHENTICATOR_FACTORY_H_
......@@ -177,6 +177,8 @@
'protocol/pseudotcp_adapter.h',
'protocol/pseudotcp_channel_factory.cc',
'protocol/pseudotcp_channel_factory.h',
'protocol/rejecting_authenticator.cc',
'protocol/rejecting_authenticator.h',
'protocol/secure_channel_factory.cc',
'protocol/secure_channel_factory.h',
'protocol/session.h',
......
......@@ -353,7 +353,7 @@ class ProtocolPerfTest
host_secret.value = "123456";
scoped_ptr<protocol::AuthenticatorFactory> auth_factory =
protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
true, kHostOwner, host_cert, key_pair, host_secret, nullptr);
true, kHostOwner, host_cert, key_pair, "", host_secret, nullptr);
host_->SetAuthenticatorFactory(std::move(auth_factory));
host_->AddStatusObserver(this);
......
......@@ -62564,6 +62564,8 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
<int value="313" label="Default key generation setting"/>
<int value="314" label="Allow key generation on these sites"/>
<int value="315" label="Block key generation on these sites"/>
<int value="316"
label="Configure the required domain name for remote access clients"/>
</enum>
<enum name="EnterprisePolicyInvalidations" type="int">
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment