1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/policy/cloud/component_cloud_policy_store.h"
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chrome/browser/policy/cloud/cloud_policy_constants.h"
14 #include "chrome/browser/policy/cloud/cloud_policy_validator.h"
15 #include "chrome/browser/policy/cloud/resource_cache.h"
16 #include "chrome/browser/policy/external_data_fetcher.h"
17 #include "chrome/browser/policy/policy_map.h"
18 #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h"
19 #include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h"
22 namespace em = enterprise_management;
28 const char kValue[] = "Value";
29 const char kLevel[] = "Level";
30 const char kRecommended[] = "Recommended";
32 const struct DomainConstants {
34 const char* proto_cache_key;
35 const char* data_cache_key;
36 const char* policy_type;
39 POLICY_DOMAIN_EXTENSIONS,
41 "extension-policy-data",
42 dm_protocol::kChromeExtensionPolicyType,
46 const DomainConstants* GetDomainConstants(PolicyDomain domain) {
47 for (size_t i = 0; i < arraysize(kDomains); ++i) {
48 if (kDomains[i].domain == domain)
54 const DomainConstants* GetDomainConstantsForType(const std::string& type) {
55 for (size_t i = 0; i < arraysize(kDomains); ++i) {
56 if (kDomains[i].policy_type == type)
64 ComponentCloudPolicyStore::Delegate::~Delegate() {}
66 ComponentCloudPolicyStore::ComponentCloudPolicyStore(
69 : delegate_(delegate),
72 ComponentCloudPolicyStore::~ComponentCloudPolicyStore() {
73 DCHECK(CalledOnValidThread());
77 bool ComponentCloudPolicyStore::SupportsDomain(PolicyDomain domain) {
78 return GetDomainConstants(domain) != NULL;
82 bool ComponentCloudPolicyStore::GetPolicyType(PolicyDomain domain,
83 std::string* policy_type) {
84 const DomainConstants* constants = GetDomainConstants(domain);
86 *policy_type = constants->policy_type;
87 return constants != NULL;
91 bool ComponentCloudPolicyStore::GetPolicyDomain(const std::string& policy_type,
92 PolicyDomain* domain) {
93 const DomainConstants* constants = GetDomainConstantsForType(policy_type);
95 *domain = constants->domain;
96 return constants != NULL;
99 const std::string& ComponentCloudPolicyStore::GetCachedHash(
100 const PolicyNamespace& ns) const {
101 DCHECK(CalledOnValidThread());
102 std::map<PolicyNamespace, std::string>::const_iterator it =
103 cached_hashes_.find(ns);
104 return it == cached_hashes_.end() ? EmptyString() : it->second;
107 void ComponentCloudPolicyStore::SetCredentials(const std::string& username,
108 const std::string& dm_token) {
109 DCHECK(CalledOnValidThread());
110 DCHECK(username_.empty() || username == username_);
111 DCHECK(dm_token_.empty() || dm_token == dm_token_);
112 username_ = username;
113 dm_token_ = dm_token;
116 void ComponentCloudPolicyStore::Load() {
117 DCHECK(CalledOnValidThread());
118 typedef std::map<std::string, std::string> ContentMap;
120 // Load all cached policy protobufs for each domain.
121 for (size_t domain = 0; domain < arraysize(kDomains); ++domain) {
122 const DomainConstants& constants = kDomains[domain];
124 cache_->LoadAllSubkeys(constants.proto_cache_key, &protos);
125 for (ContentMap::iterator it = protos.begin(); it != protos.end(); ++it) {
126 const std::string& id(it->first);
127 PolicyNamespace ns(constants.domain, id);
129 // Validate each protobuf.
130 scoped_ptr<em::PolicyFetchResponse> proto(new em::PolicyFetchResponse);
131 em::ExternalPolicyData payload;
132 if (!proto->ParseFromString(it->second) ||
134 proto.Pass(), constants.policy_type, id, &payload, NULL)) {
139 // The protobuf looks good; load the policy data.
142 if (cache_->Load(constants.data_cache_key, id, &data) &&
143 ValidateData(data, payload.secure_hash(), &policy)) {
144 // The data is also good; expose the policies.
145 policy_bundle_.Get(ns).Swap(&policy);
146 cached_hashes_[ns] = payload.secure_hash();
148 // The data for this proto couldn't be loaded or is corrupted.
155 bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
156 const std::string& serialized_policy,
157 const std::string& secure_hash,
158 const std::string& data) {
159 DCHECK(CalledOnValidThread());
160 const DomainConstants* constants = GetDomainConstants(ns.domain);
162 // |serialized_policy| has already been validated; validate the data now.
163 if (!constants || !ValidateData(data, secure_hash, &policy))
166 // Flush the proto and the data to the cache.
167 cache_->Store(constants->proto_cache_key, ns.component_id, serialized_policy);
168 cache_->Store(constants->data_cache_key, ns.component_id, data);
169 // And expose the policy.
170 policy_bundle_.Get(ns).Swap(&policy);
171 cached_hashes_[ns] = secure_hash;
172 delegate_->OnComponentCloudPolicyStoreUpdated();
176 void ComponentCloudPolicyStore::Delete(const PolicyNamespace& ns) {
177 DCHECK(CalledOnValidThread());
178 const DomainConstants* constants = GetDomainConstants(ns.domain);
182 cache_->Delete(constants->proto_cache_key, ns.component_id);
183 cache_->Delete(constants->data_cache_key, ns.component_id);
185 if (!policy_bundle_.Get(ns).empty()) {
186 policy_bundle_.Get(ns).Clear();
187 delegate_->OnComponentCloudPolicyStoreUpdated();
191 void ComponentCloudPolicyStore::Purge(PolicyDomain domain,
192 const std::set<std::string>& keep) {
193 DCHECK(CalledOnValidThread());
194 const DomainConstants* constants = GetDomainConstants(domain);
198 cache_->PurgeOtherSubkeys(constants->proto_cache_key, keep);
199 cache_->PurgeOtherSubkeys(constants->data_cache_key, keep);
201 // Stop serving policies for purged namespaces.
202 bool purged_current_policies = false;
203 for (PolicyBundle::const_iterator it = policy_bundle_.begin();
204 it != policy_bundle_.end(); ++it) {
205 if (it->first.domain == domain &&
206 keep.find(it->first.component_id) == keep.end() &&
207 !policy_bundle_.Get(it->first).empty()) {
208 policy_bundle_.Get(it->first).Clear();
209 purged_current_policies = true;
213 // Purge cached hashes, so that those namespaces can be fetched again if the
214 // policy state changes.
215 std::map<PolicyNamespace, std::string>::iterator it = cached_hashes_.begin();
216 while (it != cached_hashes_.end()) {
217 if (it->first.domain == domain &&
218 keep.find(it->first.component_id) == keep.end()) {
219 std::map<PolicyNamespace, std::string>::iterator prev = it;
221 cached_hashes_.erase(prev);
227 if (purged_current_policies)
228 delegate_->OnComponentCloudPolicyStoreUpdated();
231 bool ComponentCloudPolicyStore::ValidatePolicy(
232 scoped_ptr<em::PolicyFetchResponse> proto,
234 em::ExternalPolicyData* payload) {
235 em::PolicyData policy_data;
237 proto.Pass(), std::string(), std::string(), payload, &policy_data)) {
241 if (!policy_data.has_policy_type())
244 const DomainConstants* constants =
245 GetDomainConstantsForType(policy_data.policy_type());
246 if (!constants || !policy_data.has_settings_entity_id())
249 ns->domain = constants->domain;
250 ns->component_id = policy_data.settings_entity_id();
254 bool ComponentCloudPolicyStore::ValidateProto(
255 scoped_ptr<em::PolicyFetchResponse> proto,
256 const std::string& policy_type,
257 const std::string& settings_entity_id,
258 em::ExternalPolicyData* payload,
259 em::PolicyData* policy_data) {
260 if (username_.empty() || dm_token_.empty())
263 scoped_ptr<ComponentCloudPolicyValidator> validator(
264 ComponentCloudPolicyValidator::Create(
265 proto.Pass(), scoped_refptr<base::SequencedTaskRunner>()));
266 validator->ValidateUsername(username_);
267 validator->ValidateDMToken(dm_token_,
268 ComponentCloudPolicyValidator::DM_TOKEN_REQUIRED);
269 if (!policy_type.empty())
270 validator->ValidatePolicyType(policy_type);
271 if (!settings_entity_id.empty())
272 validator->ValidateSettingsEntityId(settings_entity_id);
273 validator->ValidatePayload();
274 // TODO(joaodasilva): validate signature.
275 validator->RunValidation();
276 if (!validator->success())
279 em::ExternalPolicyData* data = validator->payload().get();
280 // The download URL must be empty, or must be a valid URL.
281 // An empty download URL signals that this component doesn't have cloud
282 // policy, or that the policy has been removed.
283 if (data->has_download_url() && !data->download_url().empty()) {
284 if (!GURL(data->download_url()).is_valid() ||
285 !data->has_secure_hash() ||
286 data->secure_hash().empty()) {
289 } else if (data->has_secure_hash()) {
294 payload->Swap(validator->payload().get());
296 policy_data->Swap(validator->policy_data().get());
300 bool ComponentCloudPolicyStore::ValidateData(
301 const std::string& data,
302 const std::string& secure_hash,
304 return base::SHA1HashString(data) == secure_hash && ParsePolicy(data, policy);
307 bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
309 scoped_ptr<base::Value> json(base::JSONReader::Read(
310 data, base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN));
311 base::DictionaryValue* dict = NULL;
312 if (!json || !json->GetAsDictionary(&dict))
315 // Each top-level key maps a policy name to its description.
317 // Each description is an object that contains the policy value under the
318 // "Value" key. The optional "Level" key is either "Mandatory" (default) or
320 for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
321 base::DictionaryValue* description = NULL;
322 if (!dict->GetDictionaryWithoutPathExpansion(it.key(), &description))
325 scoped_ptr<base::Value> value;
326 if (!description->RemoveWithoutPathExpansion(kValue, &value))
329 PolicyLevel level = POLICY_LEVEL_MANDATORY;
330 std::string level_string;
331 if (description->GetStringWithoutPathExpansion(kLevel, &level_string) &&
332 level_string == kRecommended) {
333 level = POLICY_LEVEL_RECOMMENDED;
336 // If policy for components is ever used for device-level settings then
337 // this must support a configurable scope; assuming POLICY_SCOPE_USER is
339 policy->Set(it.key(), level, POLICY_SCOPE_USER, value.release(), NULL);
345 } // namespace policy