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