Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(555)

Unified Diff: components/policy/core/common/cloud/component_cloud_policy_store.cc

Issue 2493603002: Implement component cloud policy signature validation (Closed)
Patch Set: Add comment Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/policy/core/common/cloud/component_cloud_policy_store.cc
diff --git a/components/policy/core/common/cloud/component_cloud_policy_store.cc b/components/policy/core/common/cloud/component_cloud_policy_store.cc
index 4a330514a5e3f71a3bfc20184461852c81560195..93617d8d7e983affad3de065024a0e407013ca0f 100644
--- a/components/policy/core/common/cloud/component_cloud_policy_store.cc
+++ b/components/policy/core/common/cloud/component_cloud_policy_store.cc
@@ -5,6 +5,8 @@
#include "components/policy/core/common/cloud/component_cloud_policy_store.h"
#include <stddef.h>
+#include <stdint.h>
+
#include <utility>
#include "base/callback.h"
@@ -21,6 +23,7 @@
#include "components/policy/proto/chrome_extension_policy.pb.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "crypto/sha2.h"
+#include "google_apis/gaia/gaia_auth_util.h"
#include "url/gurl.h"
namespace em = enterprise_management;
@@ -54,30 +57,32 @@ const struct DomainConstants {
};
const DomainConstants* GetDomainConstants(PolicyDomain domain) {
- for (size_t i = 0; i < arraysize(kDomains); ++i) {
- if (kDomains[i].domain == domain)
- return &kDomains[i];
+ for (const DomainConstants& constants : kDomains) {
+ if (constants.domain == domain)
+ return &constants;
}
- return NULL;
+ return nullptr;
}
const DomainConstants* GetDomainConstantsForType(const std::string& type) {
- for (size_t i = 0; i < arraysize(kDomains); ++i) {
- if (kDomains[i].policy_type == type)
- return &kDomains[i];
+ for (const DomainConstants& constants : kDomains) {
+ if (constants.policy_type == type)
+ return &constants;
}
- return NULL;
+ return nullptr;
+}
+
+base::Time GetTimeFromPolicyTimestamp(int64_t timestamp) {
+ return base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(timestamp);
}
} // namespace
ComponentCloudPolicyStore::Delegate::~Delegate() {}
-ComponentCloudPolicyStore::ComponentCloudPolicyStore(
- Delegate* delegate,
- ResourceCache* cache)
- : delegate_(delegate),
- cache_(cache) {
+ComponentCloudPolicyStore::ComponentCloudPolicyStore(Delegate* delegate,
+ ResourceCache* cache)
+ : delegate_(delegate), cache_(cache) {
// Allow the store to be created on a different thread than the thread that
// will end up using it.
DetachFromThread();
@@ -89,7 +94,7 @@ ComponentCloudPolicyStore::~ComponentCloudPolicyStore() {
// static
bool ComponentCloudPolicyStore::SupportsDomain(PolicyDomain domain) {
- return GetDomainConstants(domain) != NULL;
+ return GetDomainConstants(domain) != nullptr;
}
// static
@@ -98,7 +103,7 @@ bool ComponentCloudPolicyStore::GetPolicyType(PolicyDomain domain,
const DomainConstants* constants = GetDomainConstants(domain);
if (constants)
*policy_type = constants->policy_type;
- return constants != NULL;
+ return constants != nullptr;
}
// static
@@ -107,7 +112,7 @@ bool ComponentCloudPolicyStore::GetPolicyDomain(const std::string& policy_type,
const DomainConstants* constants = GetDomainConstantsForType(policy_type);
if (constants)
*domain = constants->domain;
- return constants != NULL;
+ return constants != nullptr;
}
const std::string& ComponentCloudPolicyStore::GetCachedHash(
@@ -119,12 +124,19 @@ const std::string& ComponentCloudPolicyStore::GetCachedHash(
}
void ComponentCloudPolicyStore::SetCredentials(const std::string& username,
- const std::string& dm_token) {
+ const std::string& dm_token,
+ const std::string& device_id,
+ const std::string& public_key,
+ int public_key_version) {
DCHECK(CalledOnValidThread());
DCHECK(username_.empty() || username == username_);
DCHECK(dm_token_.empty() || dm_token == dm_token_);
+ DCHECK(device_id_.empty() || device_id == device_id_);
username_ = username;
dm_token_ = dm_token;
+ device_id_ = device_id;
+ public_key_ = public_key;
+ public_key_version_ = public_key_version;
}
void ComponentCloudPolicyStore::Load() {
@@ -132,21 +144,20 @@ void ComponentCloudPolicyStore::Load() {
typedef std::map<std::string, std::string> ContentMap;
// Load all cached policy protobufs for each domain.
- for (size_t domain = 0; domain < arraysize(kDomains); ++domain) {
- const DomainConstants& constants = kDomains[domain];
+ for (const DomainConstants& constants : kDomains) {
ContentMap protos;
cache_->LoadAllSubkeys(constants.proto_cache_key, &protos);
for (ContentMap::iterator it = protos.begin(); it != protos.end(); ++it) {
const std::string& id(it->first);
- PolicyNamespace ns(constants.domain, id);
+ const PolicyNamespace ns(constants.domain, id);
// Validate each protobuf.
std::unique_ptr<em::PolicyFetchResponse> proto(
new em::PolicyFetchResponse);
em::ExternalPolicyData payload;
+ em::PolicyData policy_data;
if (!proto->ParseFromString(it->second) ||
- !ValidateProto(std::move(proto), constants.policy_type, id, &payload,
- NULL)) {
+ !ValidatePolicy(ns, std::move(proto), &policy_data, &payload)) {
Delete(ns);
continue;
}
@@ -159,6 +170,8 @@ void ComponentCloudPolicyStore::Load() {
// The data is also good; expose the policies.
policy_bundle_.Get(ns).Swap(&policy);
cached_hashes_[ns] = payload.secure_hash();
+ stored_policy_times_[ns] =
+ GetTimeFromPolicyTimestamp(policy_data.timestamp());
} else {
// The data for this proto couldn't be loaded or is corrupted.
Delete(ns);
@@ -167,15 +180,19 @@ void ComponentCloudPolicyStore::Load() {
}
}
-bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
- const std::string& serialized_policy,
- const std::string& secure_hash,
- const std::string& data) {
+bool ComponentCloudPolicyStore::Store(
+ const PolicyNamespace& ns,
+ const std::string& serialized_policy,
+ std::unique_ptr<em::PolicyData> policy_data,
+ const std::string& secure_hash,
+ const std::string& data) {
DCHECK(CalledOnValidThread());
const DomainConstants* constants = GetDomainConstants(ns.domain);
PolicyMap policy;
// |serialized_policy| has already been validated; validate the data now.
- if (!constants || !ValidateData(data, secure_hash, &policy))
+ if (!constants)
+ return false;
+ if (!ValidateData(data, secure_hash, &policy))
return false;
// Flush the proto and the data to the cache.
@@ -184,6 +201,8 @@ bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
// And expose the policy.
policy_bundle_.Get(ns).Swap(&policy);
cached_hashes_[ns] = secure_hash;
+ stored_policy_times_[ns] =
+ GetTimeFromPolicyTimestamp(policy_data->timestamp());
delegate_->OnComponentCloudPolicyStoreUpdated();
return true;
}
@@ -230,10 +249,13 @@ void ComponentCloudPolicyStore::Purge(
// policy state changes.
std::map<PolicyNamespace, std::string>::iterator it = cached_hashes_.begin();
while (it != cached_hashes_.end()) {
- if (it->first.domain == domain && filter.Run(it->first.component_id)) {
+ const PolicyNamespace ns(it->first);
+ if (ns.domain == domain && filter.Run(ns.component_id)) {
std::map<PolicyNamespace, std::string>::iterator prev = it;
++it;
cached_hashes_.erase(prev);
+ DCHECK(stored_policy_times_.count(ns));
+ stored_policy_times_.erase(ns);
} else {
++it;
}
@@ -244,11 +266,12 @@ void ComponentCloudPolicyStore::Purge(
}
void ComponentCloudPolicyStore::Clear() {
- for (size_t i = 0; i < arraysize(kDomains); ++i) {
- cache_->Clear(kDomains[i].proto_cache_key);
- cache_->Clear(kDomains[i].data_cache_key);
+ for (const DomainConstants& constants : kDomains) {
+ cache_->Clear(constants.proto_cache_key);
+ cache_->Clear(constants.data_cache_key);
}
cached_hashes_.clear();
+ stored_policy_times_.clear();
const PolicyBundle empty_bundle;
if (!policy_bundle_.Equals(empty_bundle)) {
policy_bundle_.Clear();
@@ -257,71 +280,85 @@ void ComponentCloudPolicyStore::Clear() {
}
bool ComponentCloudPolicyStore::ValidatePolicy(
+ const PolicyNamespace& ns,
std::unique_ptr<em::PolicyFetchResponse> proto,
- PolicyNamespace* ns,
+ em::PolicyData* policy_data,
em::ExternalPolicyData* payload) {
- em::PolicyData policy_data;
- if (!ValidateProto(std::move(proto), std::string(), std::string(), payload,
- &policy_data)) {
+ std::string policy_type;
+ if (!GetPolicyType(ns.domain, &policy_type)) {
+ LOG(ERROR) << "Bad policy type";
return false;
}
- if (!policy_data.has_policy_type())
- return false;
-
- const DomainConstants* constants =
- GetDomainConstantsForType(policy_data.policy_type());
- if (!constants || !policy_data.has_settings_entity_id())
+ if (username_.empty() || dm_token_.empty() || device_id_.empty() ||
+ public_key_.empty() || public_key_version_ == -1) {
+ LOG(WARNING) << "Credentials are not loaded yet";
return false;
+ }
- ns->domain = constants->domain;
- ns->component_id = policy_data.settings_entity_id();
- return true;
-}
-
-bool ComponentCloudPolicyStore::ValidateProto(
- std::unique_ptr<em::PolicyFetchResponse> proto,
- const std::string& policy_type,
- const std::string& settings_entity_id,
- em::ExternalPolicyData* payload,
- em::PolicyData* policy_data) {
- if (username_.empty() || dm_token_.empty())
- return false;
+ // Calculate the bounds for the timestamp validation: a valid policy should be
+ // not older than the currently stored policy, and also the timestamp should
+ // not point too far in the future. This allows to prevent the rollback of the
+ // policy, together with some protection against incorrectly large timestamps
+ // that could be generated by the server due to some bug.
+ base::Time time_not_before;
+ const auto stored_policy_times_iter = stored_policy_times_.find(ns);
+ if (stored_policy_times_iter != stored_policy_times_.end())
+ time_not_before = stored_policy_times_iter->second;
+ const base::Time time_not_after = base::Time::NowFromSystemTime();
std::unique_ptr<ComponentCloudPolicyValidator> validator(
ComponentCloudPolicyValidator::Create(
std::move(proto), scoped_refptr<base::SequencedTaskRunner>()));
+ validator->ValidateTimestamp(
+ time_not_before, time_not_after,
+ CloudPolicyValidatorBase::TIMESTAMP_FULLY_VALIDATED);
validator->ValidateUsername(username_, true);
validator->ValidateDMToken(dm_token_,
ComponentCloudPolicyValidator::DM_TOKEN_REQUIRED);
- if (!policy_type.empty())
- validator->ValidatePolicyType(policy_type);
- if (!settings_entity_id.empty())
- validator->ValidateSettingsEntityId(settings_entity_id);
+ validator->ValidateDeviceId(device_id_,
+ CloudPolicyValidatorBase::DEVICE_ID_REQUIRED);
+ validator->ValidatePolicyType(policy_type);
+ validator->ValidateSettingsEntityId(ns.component_id);
validator->ValidatePayload();
- // TODO(joaodasilva): validate signature.
+ validator->ValidateSignature(public_key_);
validator->RunValidation();
if (!validator->success())
return false;
+ if (!validator->policy_data()->has_public_key_version()) {
+ LOG(ERROR) << "Public key version missing";
+ return false;
+ }
+ if (validator->policy_data()->public_key_version() != public_key_version_) {
+ LOG(ERROR) << "Wrong public key version "
+ << validator->policy_data()->public_key_version()
+ << " - expected " << public_key_version_;
+ return false;
+ }
+
em::ExternalPolicyData* data = validator->payload().get();
// The download URL must be empty, or must be a valid URL.
// An empty download URL signals that this component doesn't have cloud
// policy, or that the policy has been removed.
if (data->has_download_url() && !data->download_url().empty()) {
- if (!GURL(data->download_url()).is_valid() ||
- !data->has_secure_hash() ||
- data->secure_hash().empty()) {
+ if (!GURL(data->download_url()).is_valid()) {
+ LOG(ERROR) << "Invalid URL: " << data->download_url();
+ return false;
+ }
+ if (!data->has_secure_hash() || data->secure_hash().empty()) {
+ LOG(ERROR) << "Secure hash missing";
return false;
}
} else if (data->has_secure_hash()) {
+ LOG(ERROR) << "URL missing";
return false;
}
- if (payload)
- payload->Swap(validator->payload().get());
if (policy_data)
policy_data->Swap(validator->policy_data().get());
+ if (payload)
+ payload->Swap(validator->payload().get());
return true;
}
@@ -337,7 +374,7 @@ bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
PolicyMap* policy) {
std::unique_ptr<base::Value> json = base::JSONReader::Read(
data, base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN);
- base::DictionaryValue* dict = NULL;
+ base::DictionaryValue* dict = nullptr;
if (!json || !json->GetAsDictionary(&dict))
return false;
@@ -347,7 +384,7 @@ bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
// "Value" key. The optional "Level" key is either "Mandatory" (default) or
// "Recommended".
for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
- base::DictionaryValue* description = NULL;
+ base::DictionaryValue* description = nullptr;
if (!dict->GetDictionaryWithoutPathExpansion(it.key(), &description))
return false;

Powered by Google App Engine
This is Rietveld 408576698