Home | History | Annotate | Download | only in cloud
      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 "chrome/browser/policy/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/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"
     20 #include "url/gurl.h"
     21 
     22 namespace em = enterprise_management;
     23 
     24 namespace policy {
     25 
     26 namespace {
     27 
     28 const char kValue[] = "Value";
     29 const char kLevel[] = "Level";
     30 const char kMandatory[] = "Mandatory";
     31 const char kRecommended[] = "Recommended";
     32 
     33 const struct DomainConstants {
     34   PolicyDomain domain;
     35   const char* proto_cache_key;
     36   const char* data_cache_key;
     37   const char* policy_type;
     38 } kDomains[] = {
     39   {
     40     POLICY_DOMAIN_EXTENSIONS,
     41     "extension-policy",
     42     "extension-policy-data",
     43     dm_protocol::kChromeExtensionPolicyType,
     44   },
     45 };
     46 
     47 const DomainConstants* GetDomainConstants(PolicyDomain domain) {
     48   for (size_t i = 0; i < arraysize(kDomains); ++i) {
     49     if (kDomains[i].domain == domain)
     50       return &kDomains[i];
     51   }
     52   return NULL;
     53 }
     54 
     55 const DomainConstants* GetDomainConstantsForType(const std::string& type) {
     56   for (size_t i = 0; i < arraysize(kDomains); ++i) {
     57     if (kDomains[i].policy_type == type)
     58       return &kDomains[i];
     59   }
     60   return NULL;
     61 }
     62 
     63 }  // namespace
     64 
     65 ComponentCloudPolicyStore::Delegate::~Delegate() {}
     66 
     67 ComponentCloudPolicyStore::ComponentCloudPolicyStore(
     68     Delegate* delegate,
     69     ResourceCache* cache)
     70     : delegate_(delegate),
     71       cache_(cache) {}
     72 
     73 ComponentCloudPolicyStore::~ComponentCloudPolicyStore() {
     74   DCHECK(CalledOnValidThread());
     75 }
     76 
     77 // static
     78 bool ComponentCloudPolicyStore::SupportsDomain(PolicyDomain domain) {
     79   return GetDomainConstants(domain) != NULL;
     80 }
     81 
     82 // static
     83 bool ComponentCloudPolicyStore::GetPolicyType(PolicyDomain domain,
     84                                               std::string* policy_type) {
     85   const DomainConstants* constants = GetDomainConstants(domain);
     86   if (constants)
     87     *policy_type = constants->policy_type;
     88   return constants != NULL;
     89 }
     90 
     91 // static
     92 bool ComponentCloudPolicyStore::GetPolicyDomain(const std::string& policy_type,
     93                                                 PolicyDomain* domain) {
     94   const DomainConstants* constants = GetDomainConstantsForType(policy_type);
     95   if (constants)
     96     *domain = constants->domain;
     97   return constants != NULL;
     98 }
     99 
    100 const std::string& ComponentCloudPolicyStore::GetCachedHash(
    101     const PolicyNamespace& ns) const {
    102   DCHECK(CalledOnValidThread());
    103   std::map<PolicyNamespace, std::string>::const_iterator it =
    104       cached_hashes_.find(ns);
    105   return it == cached_hashes_.end() ? EmptyString() : it->second;
    106 }
    107 
    108 void ComponentCloudPolicyStore::SetCredentials(const std::string& username,
    109                                                const std::string& dm_token) {
    110   DCHECK(CalledOnValidThread());
    111   DCHECK(username_.empty() || username == username_);
    112   DCHECK(dm_token_.empty() || dm_token == dm_token_);
    113   username_ = username;
    114   dm_token_ = dm_token;
    115 }
    116 
    117 void ComponentCloudPolicyStore::Load() {
    118   DCHECK(CalledOnValidThread());
    119   typedef std::map<std::string, std::string> ContentMap;
    120 
    121   // Load all cached policy protobufs for each domain.
    122   for (size_t domain = 0; domain < arraysize(kDomains); ++domain) {
    123     const DomainConstants& constants = kDomains[domain];
    124     ContentMap protos;
    125     cache_->LoadAllSubkeys(constants.proto_cache_key, &protos);
    126     for (ContentMap::iterator it = protos.begin(); it != protos.end(); ++it) {
    127       const std::string& id(it->first);
    128       PolicyNamespace ns(constants.domain, id);
    129 
    130       // Validate each protobuf.
    131       scoped_ptr<em::PolicyFetchResponse> proto(new em::PolicyFetchResponse);
    132       em::ExternalPolicyData payload;
    133       if (!proto->ParseFromString(it->second) ||
    134           !ValidateProto(
    135               proto.Pass(), constants.policy_type, id, &payload, NULL)) {
    136         Delete(ns);
    137         continue;
    138       }
    139 
    140       // The protobuf looks good; load the policy data.
    141       std::string data;
    142       PolicyMap policy;
    143       if (cache_->Load(constants.data_cache_key, id, &data) &&
    144           ValidateData(data, payload.secure_hash(), &policy)) {
    145         // The data is also good; expose the policies.
    146         policy_bundle_.Get(ns).Swap(&policy);
    147         cached_hashes_[ns] = payload.secure_hash();
    148       } else {
    149         // The data for this proto couldn't be loaded or is corrupted.
    150         Delete(ns);
    151       }
    152     }
    153   }
    154 }
    155 
    156 bool ComponentCloudPolicyStore::Store(const PolicyNamespace& ns,
    157                                       const std::string& serialized_policy,
    158                                       const std::string& secure_hash,
    159                                       const std::string& data) {
    160   DCHECK(CalledOnValidThread());
    161   const DomainConstants* constants = GetDomainConstants(ns.domain);
    162   PolicyMap policy;
    163   // |serialized_policy| has already been validated; validate the data now.
    164   if (!constants || !ValidateData(data, secure_hash, &policy))
    165     return false;
    166 
    167   // Flush the proto and the data to the cache.
    168   cache_->Store(constants->proto_cache_key, ns.component_id, serialized_policy);
    169   cache_->Store(constants->data_cache_key, ns.component_id, data);
    170   // And expose the policy.
    171   policy_bundle_.Get(ns).Swap(&policy);
    172   cached_hashes_[ns] = secure_hash;
    173   delegate_->OnComponentCloudPolicyStoreUpdated();
    174   return true;
    175 }
    176 
    177 void ComponentCloudPolicyStore::Delete(const PolicyNamespace& ns) {
    178   DCHECK(CalledOnValidThread());
    179   const DomainConstants* constants = GetDomainConstants(ns.domain);
    180   if (!constants)
    181     return;
    182 
    183   cache_->Delete(constants->proto_cache_key, ns.component_id);
    184   cache_->Delete(constants->data_cache_key, ns.component_id);
    185 
    186   if (!policy_bundle_.Get(ns).empty()) {
    187     policy_bundle_.Get(ns).Clear();
    188     delegate_->OnComponentCloudPolicyStoreUpdated();
    189   }
    190 }
    191 
    192 void ComponentCloudPolicyStore::Purge(PolicyDomain domain,
    193                                       const std::set<std::string>& keep) {
    194   DCHECK(CalledOnValidThread());
    195   const DomainConstants* constants = GetDomainConstants(domain);
    196   if (!constants)
    197     return;
    198 
    199   cache_->PurgeOtherSubkeys(constants->proto_cache_key, keep);
    200   cache_->PurgeOtherSubkeys(constants->data_cache_key, keep);
    201 
    202   // Stop serving policies for purged namespaces.
    203   bool purged_current_policies = false;
    204   for (PolicyBundle::const_iterator it = policy_bundle_.begin();
    205        it != policy_bundle_.end(); ++it) {
    206     if (it->first.domain == domain &&
    207         keep.find(it->first.component_id) == keep.end() &&
    208         !policy_bundle_.Get(it->first).empty()) {
    209       policy_bundle_.Get(it->first).Clear();
    210       purged_current_policies = true;
    211     }
    212   }
    213 
    214   // Purge cached hashes, so that those namespaces can be fetched again if the
    215   // policy state changes.
    216   std::map<PolicyNamespace, std::string>::iterator it = cached_hashes_.begin();
    217   while (it != cached_hashes_.end()) {
    218     if (it->first.domain == domain &&
    219         keep.find(it->first.component_id) == keep.end()) {
    220       std::map<PolicyNamespace, std::string>::iterator prev = it;
    221       ++it;
    222       cached_hashes_.erase(prev);
    223     } else {
    224       ++it;
    225     }
    226   }
    227 
    228   if (purged_current_policies)
    229     delegate_->OnComponentCloudPolicyStoreUpdated();
    230 }
    231 
    232 bool ComponentCloudPolicyStore::ValidatePolicy(
    233     scoped_ptr<em::PolicyFetchResponse> proto,
    234     PolicyNamespace* ns,
    235     em::ExternalPolicyData* payload) {
    236   em::PolicyData policy_data;
    237   if (!ValidateProto(
    238           proto.Pass(), std::string(), std::string(), payload, &policy_data)) {
    239     return false;
    240   }
    241 
    242   if (!policy_data.has_policy_type())
    243     return false;
    244 
    245   const DomainConstants* constants =
    246       GetDomainConstantsForType(policy_data.policy_type());
    247   if (!constants || !policy_data.has_settings_entity_id())
    248     return false;
    249 
    250   ns->domain = constants->domain;
    251   ns->component_id = policy_data.settings_entity_id();
    252   return true;
    253 }
    254 
    255 bool ComponentCloudPolicyStore::ValidateProto(
    256     scoped_ptr<em::PolicyFetchResponse> proto,
    257     const std::string& policy_type,
    258     const std::string& settings_entity_id,
    259     em::ExternalPolicyData* payload,
    260     em::PolicyData* policy_data) {
    261   if (username_.empty() || dm_token_.empty())
    262     return false;
    263 
    264   scoped_ptr<ComponentCloudPolicyValidator> validator(
    265       ComponentCloudPolicyValidator::Create(proto.Pass()));
    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())
    277     return false;
    278 
    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()) {
    287       return false;
    288     }
    289   } else if (data->has_secure_hash()) {
    290     return false;
    291   }
    292 
    293   if (payload)
    294     payload->Swap(validator->payload().get());
    295   if (policy_data)
    296     policy_data->Swap(validator->policy_data().get());
    297   return true;
    298 }
    299 
    300 bool ComponentCloudPolicyStore::ValidateData(
    301     const std::string& data,
    302     const std::string& secure_hash,
    303     PolicyMap* policy) {
    304   return base::SHA1HashString(data) == secure_hash && ParsePolicy(data, policy);
    305 }
    306 
    307 bool ComponentCloudPolicyStore::ParsePolicy(const std::string& data,
    308                                             PolicyMap* policy) {
    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))
    313     return false;
    314 
    315   // Each top-level key maps a policy name to its description.
    316   //
    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
    319   // "Recommended".
    320   for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
    321     base::DictionaryValue* description = NULL;
    322     if (!dict->GetDictionaryWithoutPathExpansion(it.key(), &description))
    323       return false;
    324 
    325     scoped_ptr<base::Value> value;
    326     if (!description->RemoveWithoutPathExpansion(kValue, &value))
    327       return false;
    328 
    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;
    334     }
    335 
    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
    338     // fine for now.
    339     policy->Set(it.key(), level, POLICY_SCOPE_USER, value.release(), NULL);
    340   }
    341 
    342   return true;
    343 }
    344 
    345 }  // namespace policy
    346