• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/policy/core/common/cloud/component_cloud_policy_store.h"
6 
7 #include "base/callback.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
13 #include "components/policy/core/common/cloud/cloud_policy_validator.h"
14 #include "components/policy/core/common/external_data_fetcher.h"
15 #include "components/policy/core/common/policy_map.h"
16 #include "crypto/sha2.h"
17 #include "policy/proto/chrome_extension_policy.pb.h"
18 #include "policy/proto/device_management_backend.pb.h"
19 #include "url/gurl.h"
20 
21 namespace em = enterprise_management;
22 
23 namespace policy {
24 
25 namespace {
26 
27 const char kValue[] = "Value";
28 const char kLevel[] = "Level";
29 const char kRecommended[] = "Recommended";
30 
31 const struct DomainConstants {
32   PolicyDomain domain;
33   const char* proto_cache_key;
34   const char* data_cache_key;
35   const char* policy_type;
36 } kDomains[] = {
37   {
38     POLICY_DOMAIN_EXTENSIONS,
39     "extension-policy",
40     "extension-policy-data",
41     dm_protocol::kChromeExtensionPolicyType,
42   },
43 };
44 
GetDomainConstants(PolicyDomain domain)45 const DomainConstants* GetDomainConstants(PolicyDomain domain) {
46   for (size_t i = 0; i < arraysize(kDomains); ++i) {
47     if (kDomains[i].domain == domain)
48       return &kDomains[i];
49   }
50   return NULL;
51 }
52 
GetDomainConstantsForType(const std::string & type)53 const DomainConstants* GetDomainConstantsForType(const std::string& type) {
54   for (size_t i = 0; i < arraysize(kDomains); ++i) {
55     if (kDomains[i].policy_type == type)
56       return &kDomains[i];
57   }
58   return NULL;
59 }
60 
61 }  // namespace
62 
~Delegate()63 ComponentCloudPolicyStore::Delegate::~Delegate() {}
64 
ComponentCloudPolicyStore(Delegate * delegate,ResourceCache * cache)65 ComponentCloudPolicyStore::ComponentCloudPolicyStore(
66     Delegate* delegate,
67     ResourceCache* cache)
68     : delegate_(delegate),
69       cache_(cache) {
70   // Allow the store to be created on a different thread than the thread that
71   // will end up using it.
72   DetachFromThread();
73 }
74 
~ComponentCloudPolicyStore()75 ComponentCloudPolicyStore::~ComponentCloudPolicyStore() {
76   DCHECK(CalledOnValidThread());
77 }
78 
79 // static
SupportsDomain(PolicyDomain domain)80 bool ComponentCloudPolicyStore::SupportsDomain(PolicyDomain domain) {
81   return GetDomainConstants(domain) != NULL;
82 }
83 
84 // static
GetPolicyType(PolicyDomain domain,std::string * policy_type)85 bool ComponentCloudPolicyStore::GetPolicyType(PolicyDomain domain,
86                                               std::string* policy_type) {
87   const DomainConstants* constants = GetDomainConstants(domain);
88   if (constants)
89     *policy_type = constants->policy_type;
90   return constants != NULL;
91 }
92 
93 // static
GetPolicyDomain(const std::string & policy_type,PolicyDomain * domain)94 bool ComponentCloudPolicyStore::GetPolicyDomain(const std::string& policy_type,
95                                                 PolicyDomain* domain) {
96   const DomainConstants* constants = GetDomainConstantsForType(policy_type);
97   if (constants)
98     *domain = constants->domain;
99   return constants != NULL;
100 }
101 
GetCachedHash(const PolicyNamespace & ns) const102 const std::string& ComponentCloudPolicyStore::GetCachedHash(
103     const PolicyNamespace& ns) const {
104   DCHECK(CalledOnValidThread());
105   std::map<PolicyNamespace, std::string>::const_iterator it =
106       cached_hashes_.find(ns);
107   return it == cached_hashes_.end() ? base::EmptyString() : it->second;
108 }
109 
SetCredentials(const std::string & username,const std::string & dm_token)110 void ComponentCloudPolicyStore::SetCredentials(const std::string& username,
111                                                const std::string& dm_token) {
112   DCHECK(CalledOnValidThread());
113   DCHECK(username_.empty() || username == username_);
114   DCHECK(dm_token_.empty() || dm_token == dm_token_);
115   username_ = username;
116   dm_token_ = dm_token;
117 }
118 
Load()119 void ComponentCloudPolicyStore::Load() {
120   DCHECK(CalledOnValidThread());
121   typedef std::map<std::string, std::string> ContentMap;
122 
123   // Load all cached policy protobufs for each domain.
124   for (size_t domain = 0; domain < arraysize(kDomains); ++domain) {
125     const DomainConstants& constants = kDomains[domain];
126     ContentMap protos;
127     cache_->LoadAllSubkeys(constants.proto_cache_key, &protos);
128     for (ContentMap::iterator it = protos.begin(); it != protos.end(); ++it) {
129       const std::string& id(it->first);
130       PolicyNamespace ns(constants.domain, id);
131 
132       // Validate each protobuf.
133       scoped_ptr<em::PolicyFetchResponse> proto(new em::PolicyFetchResponse);
134       em::ExternalPolicyData payload;
135       if (!proto->ParseFromString(it->second) ||
136           !ValidateProto(
137               proto.Pass(), constants.policy_type, id, &payload, NULL)) {
138         Delete(ns);
139         continue;
140       }
141 
142       // The protobuf looks good; load the policy data.
143       std::string data;
144       PolicyMap policy;
145       if (cache_->Load(constants.data_cache_key, id, &data) &&
146           ValidateData(data, payload.secure_hash(), &policy)) {
147         // The data is also good; expose the policies.
148         policy_bundle_.Get(ns).Swap(&policy);
149         cached_hashes_[ns] = payload.secure_hash();
150       } else {
151         // The data for this proto couldn't be loaded or is corrupted.
152         Delete(ns);
153       }
154     }
155   }
156 }
157 
Store(const PolicyNamespace & ns,const std::string & serialized_policy,const std::string & secure_hash,const std::string & data)158 bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
159                                       const std::string& serialized_policy,
160                                       const std::string& secure_hash,
161                                       const std::string& data) {
162   DCHECK(CalledOnValidThread());
163   const DomainConstants* constants = GetDomainConstants(ns.domain);
164   PolicyMap policy;
165   // |serialized_policy| has already been validated; validate the data now.
166   if (!constants || !ValidateData(data, secure_hash, &policy))
167     return false;
168 
169   // Flush the proto and the data to the cache.
170   cache_->Store(constants->proto_cache_key, ns.component_id, serialized_policy);
171   cache_->Store(constants->data_cache_key, ns.component_id, data);
172   // And expose the policy.
173   policy_bundle_.Get(ns).Swap(&policy);
174   cached_hashes_[ns] = secure_hash;
175   delegate_->OnComponentCloudPolicyStoreUpdated();
176   return true;
177 }
178 
Delete(const PolicyNamespace & ns)179 void ComponentCloudPolicyStore::Delete(const PolicyNamespace& ns) {
180   DCHECK(CalledOnValidThread());
181   const DomainConstants* constants = GetDomainConstants(ns.domain);
182   if (!constants)
183     return;
184 
185   cache_->Delete(constants->proto_cache_key, ns.component_id);
186   cache_->Delete(constants->data_cache_key, ns.component_id);
187 
188   if (!policy_bundle_.Get(ns).empty()) {
189     policy_bundle_.Get(ns).Clear();
190     delegate_->OnComponentCloudPolicyStoreUpdated();
191   }
192 }
193 
Purge(PolicyDomain domain,const ResourceCache::SubkeyFilter & filter)194 void ComponentCloudPolicyStore::Purge(
195     PolicyDomain domain,
196     const ResourceCache::SubkeyFilter& filter) {
197   DCHECK(CalledOnValidThread());
198   const DomainConstants* constants = GetDomainConstants(domain);
199   if (!constants)
200     return;
201 
202   cache_->FilterSubkeys(constants->proto_cache_key, filter);
203   cache_->FilterSubkeys(constants->data_cache_key, filter);
204 
205   // Stop serving policies for purged namespaces.
206   bool purged_current_policies = false;
207   for (PolicyBundle::const_iterator it = policy_bundle_.begin();
208        it != policy_bundle_.end(); ++it) {
209     if (it->first.domain == domain &&
210         filter.Run(it->first.component_id) &&
211         !policy_bundle_.Get(it->first).empty()) {
212       policy_bundle_.Get(it->first).Clear();
213       purged_current_policies = true;
214     }
215   }
216 
217   // Purge cached hashes, so that those namespaces can be fetched again if the
218   // policy state changes.
219   std::map<PolicyNamespace, std::string>::iterator it = cached_hashes_.begin();
220   while (it != cached_hashes_.end()) {
221     if (it->first.domain == domain && filter.Run(it->first.component_id)) {
222       std::map<PolicyNamespace, std::string>::iterator prev = it;
223       ++it;
224       cached_hashes_.erase(prev);
225     } else {
226       ++it;
227     }
228   }
229 
230   if (purged_current_policies)
231     delegate_->OnComponentCloudPolicyStoreUpdated();
232 }
233 
Clear()234 void ComponentCloudPolicyStore::Clear() {
235   for (size_t i = 0; i < arraysize(kDomains); ++i) {
236     cache_->Clear(kDomains[i].proto_cache_key);
237     cache_->Clear(kDomains[i].data_cache_key);
238   }
239   cached_hashes_.clear();
240   const PolicyBundle empty_bundle;
241   if (!policy_bundle_.Equals(empty_bundle)) {
242     policy_bundle_.Clear();
243     delegate_->OnComponentCloudPolicyStoreUpdated();
244   }
245 }
246 
ValidatePolicy(scoped_ptr<em::PolicyFetchResponse> proto,PolicyNamespace * ns,em::ExternalPolicyData * payload)247 bool ComponentCloudPolicyStore::ValidatePolicy(
248     scoped_ptr<em::PolicyFetchResponse> proto,
249     PolicyNamespace* ns,
250     em::ExternalPolicyData* payload) {
251   em::PolicyData policy_data;
252   if (!ValidateProto(
253           proto.Pass(), std::string(), std::string(), payload, &policy_data)) {
254     return false;
255   }
256 
257   if (!policy_data.has_policy_type())
258     return false;
259 
260   const DomainConstants* constants =
261       GetDomainConstantsForType(policy_data.policy_type());
262   if (!constants || !policy_data.has_settings_entity_id())
263     return false;
264 
265   ns->domain = constants->domain;
266   ns->component_id = policy_data.settings_entity_id();
267   return true;
268 }
269 
ValidateProto(scoped_ptr<em::PolicyFetchResponse> proto,const std::string & policy_type,const std::string & settings_entity_id,em::ExternalPolicyData * payload,em::PolicyData * policy_data)270 bool ComponentCloudPolicyStore::ValidateProto(
271     scoped_ptr<em::PolicyFetchResponse> proto,
272     const std::string& policy_type,
273     const std::string& settings_entity_id,
274     em::ExternalPolicyData* payload,
275     em::PolicyData* policy_data) {
276   if (username_.empty() || dm_token_.empty())
277     return false;
278 
279   scoped_ptr<ComponentCloudPolicyValidator> validator(
280       ComponentCloudPolicyValidator::Create(
281           proto.Pass(), scoped_refptr<base::SequencedTaskRunner>()));
282   validator->ValidateUsername(username_, true);
283   validator->ValidateDMToken(dm_token_,
284                              ComponentCloudPolicyValidator::DM_TOKEN_REQUIRED);
285   if (!policy_type.empty())
286     validator->ValidatePolicyType(policy_type);
287   if (!settings_entity_id.empty())
288     validator->ValidateSettingsEntityId(settings_entity_id);
289   validator->ValidatePayload();
290   // TODO(joaodasilva): validate signature.
291   validator->RunValidation();
292   if (!validator->success())
293     return false;
294 
295   em::ExternalPolicyData* data = validator->payload().get();
296   // The download URL must be empty, or must be a valid URL.
297   // An empty download URL signals that this component doesn't have cloud
298   // policy, or that the policy has been removed.
299   if (data->has_download_url() && !data->download_url().empty()) {
300     if (!GURL(data->download_url()).is_valid() ||
301         !data->has_secure_hash() ||
302         data->secure_hash().empty()) {
303       return false;
304     }
305   } else if (data->has_secure_hash()) {
306     return false;
307   }
308 
309   if (payload)
310     payload->Swap(validator->payload().get());
311   if (policy_data)
312     policy_data->Swap(validator->policy_data().get());
313   return true;
314 }
315 
ValidateData(const std::string & data,const std::string & secure_hash,PolicyMap * policy)316 bool ComponentCloudPolicyStore::ValidateData(
317     const std::string& data,
318     const std::string& secure_hash,
319     PolicyMap* policy) {
320   return crypto::SHA256HashString(data) == secure_hash &&
321       ParsePolicy(data, policy);
322 }
323 
ParsePolicy(const std::string & data,PolicyMap * policy)324 bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
325                                             PolicyMap* policy) {
326   scoped_ptr<base::Value> json(base::JSONReader::Read(
327       data, base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN));
328   base::DictionaryValue* dict = NULL;
329   if (!json || !json->GetAsDictionary(&dict))
330     return false;
331 
332   // Each top-level key maps a policy name to its description.
333   //
334   // Each description is an object that contains the policy value under the
335   // "Value" key. The optional "Level" key is either "Mandatory" (default) or
336   // "Recommended".
337   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
338     base::DictionaryValue* description = NULL;
339     if (!dict->GetDictionaryWithoutPathExpansion(it.key(), &description))
340       return false;
341 
342     scoped_ptr<base::Value> value;
343     if (!description->RemoveWithoutPathExpansion(kValue, &value))
344       return false;
345 
346     PolicyLevel level = POLICY_LEVEL_MANDATORY;
347     std::string level_string;
348     if (description->GetStringWithoutPathExpansion(kLevel, &level_string) &&
349         level_string == kRecommended) {
350       level = POLICY_LEVEL_RECOMMENDED;
351     }
352 
353     // If policy for components is ever used for device-level settings then
354     // this must support a configurable scope; assuming POLICY_SCOPE_USER is
355     // fine for now.
356     policy->Set(it.key(), level, POLICY_SCOPE_USER, value.release(), NULL);
357   }
358 
359   return true;
360 }
361 
362 }  // namespace policy
363