// Copyright (c) 2012 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.
// Most of this code is copied from:
// src/chrome/browser/policy/asynchronous_policy_loader.{h,cc}
#include "remoting/host/policy_hack/policy_watcher.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/location.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "base/values.h"
#include "remoting/host/dns_blackhole_checker.h"
#if !defined(NDEBUG)
#include "base/json/json_reader.h"
#endif
namespace remoting {
namespace policy_hack {
namespace {
// The time interval for rechecking policy. This is our fallback in case the
// delegate never reports a change to the ReloadObserver.
const int kFallbackReloadDelayMinutes = 15;
// Copies all policy values from one dictionary to another, using values from
// |default| if they are not set in |from|, or values from |bad_type_values| if
// the value in |from| has the wrong type.
scoped_ptr<base::DictionaryValue> CopyGoodValuesAndAddDefaults(
const base::DictionaryValue* from,
const base::DictionaryValue* default_values,
const base::DictionaryValue* bad_type_values) {
scoped_ptr<base::DictionaryValue> to(default_values->DeepCopy());
for (base::DictionaryValue::Iterator i(*default_values);
!i.IsAtEnd(); i.Advance()) {
const base::Value* value = NULL;
// If the policy isn't in |from|, use the default.
if (!from->Get(i.key(), &value)) {
continue;
}
// If the policy is the wrong type, use the value from |bad_type_values|.
if (!value->IsType(i.value().GetType())) {
CHECK(bad_type_values->Get(i.key(), &value));
}
to->Set(i.key(), value->DeepCopy());
}
#if !defined(NDEBUG)
// Replace values with those specified in DebugOverridePolicies, if present.
std::string policy_overrides;
if (from->GetString(PolicyWatcher::kHostDebugOverridePoliciesName,
&policy_overrides)) {
scoped_ptr<base::Value> value(base::JSONReader::Read(policy_overrides));
const base::DictionaryValue* override_values;
if (value && value->GetAsDictionary(&override_values)) {
to->MergeDictionary(override_values);
}
}
#endif // defined(NDEBUG)
return to.Pass();
}
} // namespace
const char PolicyWatcher::kNatPolicyName[] =
"RemoteAccessHostFirewallTraversal";
const char PolicyWatcher::kHostRequireTwoFactorPolicyName[] =
"RemoteAccessHostRequireTwoFactor";
const char PolicyWatcher::kHostDomainPolicyName[] =
"RemoteAccessHostDomain";
const char PolicyWatcher::kHostMatchUsernamePolicyName[] =
"RemoteAccessHostMatchUsername";
const char PolicyWatcher::kHostTalkGadgetPrefixPolicyName[] =
"RemoteAccessHostTalkGadgetPrefix";
const char PolicyWatcher::kHostRequireCurtainPolicyName[] =
"RemoteAccessHostRequireCurtain";
const char PolicyWatcher::kHostTokenUrlPolicyName[] =
"RemoteAccessHostTokenUrl";
const char PolicyWatcher::kHostTokenValidationUrlPolicyName[] =
"RemoteAccessHostTokenValidationUrl";
const char PolicyWatcher::kHostTokenValidationCertIssuerPolicyName[] =
"RemoteAccessHostTokenValidationCertificateIssuer";
const char PolicyWatcher::kHostAllowClientPairing[] =
"RemoteAccessHostAllowClientPairing";
const char PolicyWatcher::kHostAllowGnubbyAuthPolicyName[] =
"RemoteAccessHostAllowGnubbyAuth";
const char PolicyWatcher::kRelayPolicyName[] =
"RemoteAccessHostAllowRelayedConnection";
const char PolicyWatcher::kUdpPortRangePolicyName[] =
"RemoteAccessHostUdpPortRange";
const char PolicyWatcher::kHostDebugOverridePoliciesName[] =
"RemoteAccessHostDebugOverridePolicies";
PolicyWatcher::PolicyWatcher(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: task_runner_(task_runner),
old_policies_(new base::DictionaryValue()),
default_values_(new base::DictionaryValue()),
weak_factory_(this) {
// Initialize the default values for each policy.
default_values_->SetBoolean(kNatPolicyName, true);
default_values_->SetBoolean(kHostRequireTwoFactorPolicyName, false);
default_values_->SetBoolean(kHostRequireCurtainPolicyName, false);
default_values_->SetBoolean(kHostMatchUsernamePolicyName, false);
default_values_->SetString(kHostDomainPolicyName, std::string());
default_values_->SetString(kHostTalkGadgetPrefixPolicyName,
kDefaultHostTalkGadgetPrefix);
default_values_->SetString(kHostTokenUrlPolicyName, std::string());
default_values_->SetString(kHostTokenValidationUrlPolicyName, std::string());
default_values_->SetString(kHostTokenValidationCertIssuerPolicyName,
std::string());
default_values_->SetBoolean(kHostAllowClientPairing, true);
default_values_->SetBoolean(kHostAllowGnubbyAuthPolicyName, true);
default_values_->SetBoolean(kRelayPolicyName, true);
default_values_->SetString(kUdpPortRangePolicyName, "");
#if !defined(NDEBUG)
default_values_->SetString(kHostDebugOverridePoliciesName, std::string());
#endif
// Initialize the fall-back values to use for unreadable policies.
// For most policies these match the defaults.
bad_type_values_.reset(default_values_->DeepCopy());
bad_type_values_->SetBoolean(kNatPolicyName, false);
bad_type_values_->SetBoolean(kRelayPolicyName, false);
}
PolicyWatcher::~PolicyWatcher() {
}
void PolicyWatcher::StartWatching(const PolicyCallback& policy_callback) {
if (!OnPolicyWatcherThread()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&PolicyWatcher::StartWatching,
base::Unretained(this),
policy_callback));
return;
}
policy_callback_ = policy_callback;
StartWatchingInternal();
}
void PolicyWatcher::StopWatching(base::WaitableEvent* done) {
if (!OnPolicyWatcherThread()) {
task_runner_->PostTask(FROM_HERE,
base::Bind(&PolicyWatcher::StopWatching,
base::Unretained(this), done));
return;
}
StopWatchingInternal();
weak_factory_.InvalidateWeakPtrs();
policy_callback_.Reset();
done->Signal();
}
void PolicyWatcher::ScheduleFallbackReloadTask() {
DCHECK(OnPolicyWatcherThread());
ScheduleReloadTask(
base::TimeDelta::FromMinutes(kFallbackReloadDelayMinutes));
}
void PolicyWatcher::ScheduleReloadTask(const base::TimeDelta& delay) {
DCHECK(OnPolicyWatcherThread());
task_runner_->PostDelayedTask(
FROM_HERE,
base::Bind(&PolicyWatcher::Reload, weak_factory_.GetWeakPtr()),
delay);
}
const base::DictionaryValue& PolicyWatcher::Defaults() const {
return *default_values_;
}
bool PolicyWatcher::OnPolicyWatcherThread() const {
return task_runner_->BelongsToCurrentThread();
}
void PolicyWatcher::UpdatePolicies(
const base::DictionaryValue* new_policies_raw) {
DCHECK(OnPolicyWatcherThread());
// Use default values for any missing policies.
scoped_ptr<base::DictionaryValue> new_policies =
CopyGoodValuesAndAddDefaults(
new_policies_raw, default_values_.get(), bad_type_values_.get());
// Find the changed policies.
scoped_ptr<base::DictionaryValue> changed_policies(
new base::DictionaryValue());
base::DictionaryValue::Iterator iter(*new_policies);
while (!iter.IsAtEnd()) {
base::Value* old_policy;
if (!(old_policies_->Get(iter.key(), &old_policy) &&
old_policy->Equals(&iter.value()))) {
changed_policies->Set(iter.key(), iter.value().DeepCopy());
}
iter.Advance();
}
// Save the new policies.
old_policies_.swap(new_policies);
// Notify our client of the changed policies.
if (!changed_policies->empty()) {
policy_callback_.Run(changed_policies.Pass());
}
}
} // namespace policy_hack
} // namespace remoting