user_cloud_policy_store.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 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/user_cloud_policy_store.h" 6 7#include "base/bind.h" 8#include "base/file_util.h" 9#include "base/location.h" 10#include "base/metrics/histogram.h" 11#include "base/task_runner_util.h" 12#include "google_apis/gaia/gaia_auth_util.h" 13#include "policy/proto/cloud_policy.pb.h" 14#include "policy/proto/device_management_backend.pb.h" 15#include "policy/proto/policy_signing_key.pb.h" 16 17namespace em = enterprise_management; 18 19namespace policy { 20 21enum PolicyLoadStatus { 22 // Policy blob was successfully loaded and parsed. 23 LOAD_RESULT_SUCCESS, 24 25 // No previously stored policy was found. 26 LOAD_RESULT_NO_POLICY_FILE, 27 28 // Could not load the previously stored policy due to either a parse or 29 // file read error. 30 LOAD_RESULT_LOAD_ERROR, 31}; 32 33// Struct containing the result of a policy load - if |status| == 34// LOAD_RESULT_SUCCESS, |policy| is initialized from the policy file on disk. 35struct PolicyLoadResult { 36 PolicyLoadStatus status; 37 em::PolicyFetchResponse policy; 38 em::PolicySigningKey key; 39}; 40 41namespace { 42 43// Subdirectory in the user's profile for storing user policies. 44const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy"); 45// File in the above directory for storing user policy data. 46const base::FilePath::CharType kPolicyCacheFile[] = 47 FILE_PATH_LITERAL("User Policy"); 48 49// File in the above directory for storing policy signing key data. 50const base::FilePath::CharType kKeyCacheFile[] = 51 FILE_PATH_LITERAL("Signing Key"); 52 53const char kMetricPolicyHasVerifiedCachedKey[] = 54 "Enterprise.PolicyHasVerifiedCachedKey"; 55 56// Loads policy from the backing file. Returns a PolicyLoadResult with the 57// results of the fetch. 58policy::PolicyLoadResult LoadPolicyFromDisk( 59 const base::FilePath& policy_path, 60 const base::FilePath& key_path) { 61 policy::PolicyLoadResult result; 62 // If the backing file does not exist, just return. We don't verify the key 63 // path here, because the key is optional (the validation code will fail if 64 // the key does not exist but the loaded policy is unsigned). 65 if (!base::PathExists(policy_path)) { 66 result.status = policy::LOAD_RESULT_NO_POLICY_FILE; 67 return result; 68 } 69 std::string data; 70 // TODO(atwilson): Enforce a policy/key maxsize when ReadFileToString() can 71 // accept a max_size (http://crbug.com/339417). 72 if (!base::ReadFileToString(policy_path, &data) || 73 !result.policy.ParseFromString(data)) { 74 LOG(WARNING) << "Failed to read or parse policy data from " 75 << policy_path.value(); 76 result.status = policy::LOAD_RESULT_LOAD_ERROR; 77 return result; 78 } 79 80 if (!base::ReadFileToString(key_path, &data) || 81 !result.key.ParseFromString(data)) { 82 // Log an error on missing key data, but do not trigger a load failure 83 // for now since there are still old unsigned cached policy blobs in the 84 // wild with no associated key (see kMetricPolicyHasVerifiedCachedKey UMA 85 // stat below). 86 LOG(ERROR) << "Failed to read or parse key data from " << key_path.value(); 87 result.key.clear_signing_key(); 88 } 89 90 // Track the occurrence of valid cached keys - when this ratio gets high 91 // enough, we can update the code to reject unsigned policy or unverified 92 // keys. 93 UMA_HISTOGRAM_BOOLEAN(kMetricPolicyHasVerifiedCachedKey, 94 result.key.has_signing_key()); 95 96 result.status = policy::LOAD_RESULT_SUCCESS; 97 return result; 98} 99 100bool WriteStringToFile(const base::FilePath path, const std::string& data) { 101 if (!base::CreateDirectory(path.DirName())) { 102 DLOG(WARNING) << "Failed to create directory " << path.DirName().value(); 103 return false; 104 } 105 106 int size = data.size(); 107 if (file_util::WriteFile(path, data.c_str(), size) != size) { 108 DLOG(WARNING) << "Failed to write " << path.value(); 109 return false; 110 } 111 112 return true; 113} 114 115// Stores policy to the backing file (must be called via a task on 116// the background thread). 117void StorePolicyToDiskOnBackgroundThread( 118 const base::FilePath& policy_path, 119 const base::FilePath& key_path, 120 const std::string& verification_key, 121 const em::PolicyFetchResponse& policy) { 122 DVLOG(1) << "Storing policy to " << policy_path.value(); 123 std::string data; 124 if (!policy.SerializeToString(&data)) { 125 DLOG(WARNING) << "Failed to serialize policy data"; 126 return; 127 } 128 129 if (!WriteStringToFile(policy_path, data)) 130 return; 131 132 if (policy.has_new_public_key()) { 133 // Write the new public key and its verification signature/key to a file. 134 em::PolicySigningKey key_info; 135 key_info.set_signing_key(policy.new_public_key()); 136 key_info.set_signing_key_signature( 137 policy.new_public_key_verification_signature()); 138 key_info.set_verification_key(verification_key); 139 std::string key_data; 140 if (!key_info.SerializeToString(&key_data)) { 141 DLOG(WARNING) << "Failed to serialize policy signing key"; 142 return; 143 } 144 145 WriteStringToFile(key_path, key_data); 146 } 147} 148 149} // namespace 150 151UserCloudPolicyStore::UserCloudPolicyStore( 152 const base::FilePath& policy_path, 153 const base::FilePath& key_path, 154 const std::string& verification_key, 155 scoped_refptr<base::SequencedTaskRunner> background_task_runner) 156 : UserCloudPolicyStoreBase(background_task_runner), 157 weak_factory_(this), 158 policy_path_(policy_path), 159 key_path_(key_path), 160 verification_key_(verification_key) {} 161 162UserCloudPolicyStore::~UserCloudPolicyStore() {} 163 164// static 165scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create( 166 const base::FilePath& profile_path, 167 const std::string& verification_key, 168 scoped_refptr<base::SequencedTaskRunner> background_task_runner) { 169 base::FilePath policy_path = 170 profile_path.Append(kPolicyDir).Append(kPolicyCacheFile); 171 base::FilePath key_path = 172 profile_path.Append(kPolicyDir).Append(kKeyCacheFile); 173 return make_scoped_ptr(new UserCloudPolicyStore( 174 policy_path, key_path, verification_key, background_task_runner)); 175} 176 177void UserCloudPolicyStore::SetSigninUsername(const std::string& username) { 178 signin_username_ = username; 179} 180 181void UserCloudPolicyStore::LoadImmediately() { 182 DVLOG(1) << "Initiating immediate policy load from disk"; 183 // Cancel any pending Load/Store/Validate operations. 184 weak_factory_.InvalidateWeakPtrs(); 185 // Load the policy from disk... 186 PolicyLoadResult result = LoadPolicyFromDisk(policy_path_, key_path_); 187 // ...and install it, reporting success/failure to any observers. 188 PolicyLoaded(false, result); 189} 190 191void UserCloudPolicyStore::Clear() { 192 background_task_runner()->PostTask( 193 FROM_HERE, 194 base::Bind(base::IgnoreResult(&base::DeleteFile), policy_path_, false)); 195 background_task_runner()->PostTask( 196 FROM_HERE, 197 base::Bind(base::IgnoreResult(&base::DeleteFile), key_path_, false)); 198 policy_.reset(); 199 policy_map_.Clear(); 200 policy_key_.clear(); 201 NotifyStoreLoaded(); 202} 203 204void UserCloudPolicyStore::Load() { 205 DVLOG(1) << "Initiating policy load from disk"; 206 // Cancel any pending Load/Store/Validate operations. 207 weak_factory_.InvalidateWeakPtrs(); 208 209 // Start a new Load operation and have us get called back when it is 210 // complete. 211 base::PostTaskAndReplyWithResult( 212 background_task_runner(), 213 FROM_HERE, 214 base::Bind(&LoadPolicyFromDisk, policy_path_, key_path_), 215 base::Bind(&UserCloudPolicyStore::PolicyLoaded, 216 weak_factory_.GetWeakPtr(), true)); 217} 218 219void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background, 220 PolicyLoadResult result) { 221 switch (result.status) { 222 case LOAD_RESULT_LOAD_ERROR: 223 status_ = STATUS_LOAD_ERROR; 224 NotifyStoreError(); 225 break; 226 227 case LOAD_RESULT_NO_POLICY_FILE: 228 DVLOG(1) << "No policy found on disk"; 229 NotifyStoreLoaded(); 230 break; 231 232 case LOAD_RESULT_SUCCESS: { 233 // Found policy on disk - need to validate it before it can be used. 234 scoped_ptr<em::PolicyFetchResponse> cloud_policy( 235 new em::PolicyFetchResponse(result.policy)); 236 scoped_ptr<em::PolicySigningKey> key( 237 new em::PolicySigningKey(result.key)); 238 239 bool doing_key_rotation = false; 240 const std::string& verification_key = verification_key_; 241 if (!key->has_verification_key() || 242 key->verification_key() != verification_key_) { 243 // The cached key didn't match our current key, so we're doing a key 244 // rotation - make sure we request a new key from the server on our 245 // next fetch. 246 doing_key_rotation = true; 247 DLOG(WARNING) << "Verification key rotation detected"; 248 // TODO(atwilson): Add code to update |verification_key| to point to 249 // the correct key to validate the existing blob (can't do this until 250 // we've done our first key rotation). 251 } 252 253 Validate(cloud_policy.Pass(), 254 key.Pass(), 255 verification_key, 256 validate_in_background, 257 base::Bind( 258 &UserCloudPolicyStore::InstallLoadedPolicyAfterValidation, 259 weak_factory_.GetWeakPtr(), 260 doing_key_rotation, 261 result.key.has_signing_key() ? 262 result.key.signing_key() : std::string())); 263 break; 264 } 265 default: 266 NOTREACHED(); 267 } 268} 269 270void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation( 271 bool doing_key_rotation, 272 const std::string& signing_key, 273 UserCloudPolicyValidator* validator) { 274 validation_status_ = validator->status(); 275 if (!validator->success()) { 276 DVLOG(1) << "Validation failed: status=" << validation_status_; 277 status_ = STATUS_VALIDATION_ERROR; 278 NotifyStoreError(); 279 return; 280 } 281 282 DVLOG(1) << "Validation succeeded - installing policy with dm_token: " << 283 validator->policy_data()->request_token(); 284 DVLOG(1) << "Device ID: " << validator->policy_data()->device_id(); 285 286 // If we're doing a key rotation, clear the public key version so a future 287 // policy fetch will force regeneration of the keys. 288 if (doing_key_rotation) { 289 validator->policy_data()->clear_public_key_version(); 290 policy_key_.clear(); 291 } else { 292 // Policy validation succeeded, so we know the signing key is good. 293 policy_key_ = signing_key; 294 } 295 296 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 297 status_ = STATUS_OK; 298 NotifyStoreLoaded(); 299} 300 301void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) { 302 // Stop any pending requests to store policy, then validate the new policy 303 // before storing it. 304 weak_factory_.InvalidateWeakPtrs(); 305 scoped_ptr<em::PolicyFetchResponse> policy_copy( 306 new em::PolicyFetchResponse(policy)); 307 Validate(policy_copy.Pass(), 308 scoped_ptr<em::PolicySigningKey>(), 309 verification_key_, 310 true, 311 base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation, 312 weak_factory_.GetWeakPtr())); 313} 314 315void UserCloudPolicyStore::Validate( 316 scoped_ptr<em::PolicyFetchResponse> policy, 317 scoped_ptr<em::PolicySigningKey> cached_key, 318 const std::string& verification_key, 319 bool validate_in_background, 320 const UserCloudPolicyValidator::CompletionCallback& callback) { 321 322 const bool signed_policy = policy->has_policy_data_signature(); 323 324 // Configure the validator. 325 scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator( 326 policy.Pass(), 327 CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE); 328 329 // Extract the owning domain from the signed-in user (if any is set yet). 330 // If there's no owning domain, then the code just ensures that the policy 331 // is self-consistent (that the keys are signed with the same domain that the 332 // username field in the policy contains). UserPolicySigninServerBase will 333 // verify that the username matches the signed in user once profile 334 // initialization is complete (http://crbug.com/342327). 335 std::string owning_domain; 336 337 // Validate the username if the user is signed in. The signin_username_ can 338 // be empty during initial policy load because this happens before the 339 // Prefs subsystem is initialized. 340 if (!signin_username_.empty()) { 341 DVLOG(1) << "Validating username: " << signin_username_; 342 validator->ValidateUsername(signin_username_); 343 owning_domain = gaia::ExtractDomainName( 344 gaia::CanonicalizeEmail(gaia::SanitizeEmail(signin_username_))); 345 } 346 347 // There are 4 cases: 348 // 349 // 1) Validation after loading from cache with no cached key. 350 // Action: Don't validate signature (migration from previously cached 351 // unsigned blob). 352 // 353 // 2) Validation after loading from cache with a cached key 354 // Action: Validate signature on policy blob but don't allow key rotation. 355 // 356 // 3) Validation after loading new policy from the server with no cached key 357 // Action: Validate as initial key provisioning (case where we are migrating 358 // from unsigned policy) 359 // 360 // 4) Validation after loading new policy from the server with a cached key 361 // Action: Validate as normal, and allow key rotation. 362 if (cached_key) { 363 // Loading from cache should not change the cached keys. 364 DCHECK(policy_key_.empty() || policy_key_ == cached_key->signing_key()); 365 if (!signed_policy || !cached_key->has_signing_key()) { 366 // Case #1 - loading from cache with no signing key. 367 // TODO(atwilson): Reject policy with no cached key once 368 // kMetricPolicyHasVerifiedCachedKey rises to a high enough level. 369 DLOG(WARNING) << "Allowing unsigned cached blob for migration"; 370 } else { 371 // Case #2 - loading from cache with a cached key - validate the cached 372 // key, then do normal policy data signature validation using the cached 373 // key. We're loading from cache so don't allow key rotation. 374 validator->ValidateCachedKey(cached_key->signing_key(), 375 cached_key->signing_key_signature(), 376 verification_key, 377 owning_domain); 378 const bool no_rotation = false; 379 validator->ValidateSignature(cached_key->signing_key(), 380 verification_key, 381 owning_domain, 382 no_rotation); 383 } 384 } else { 385 // No passed cached_key - this is not validating the initial policy load 386 // from cache, but rather an update from the server. 387 if (policy_key_.empty()) { 388 // Case #3 - no valid existing policy key (either this is the initial 389 // policy fetch, or we're doing a key rotation), so this new policy fetch 390 // should include an initial key provision. 391 validator->ValidateInitialKey(verification_key, owning_domain); 392 } else { 393 // Case #4 - verify new policy with existing key. We always allow key 394 // rotation - the verification key will prevent invalid policy from being 395 // injected. |policy_key_| is already known to be valid, so no need to 396 // verify via ValidateCachedKey(). 397 const bool allow_rotation = true; 398 validator->ValidateSignature( 399 policy_key_, verification_key, owning_domain, allow_rotation); 400 } 401 } 402 403 if (validate_in_background) { 404 // Start validation in the background. The Validator will free itself once 405 // validation is complete. 406 validator.release()->StartValidation(callback); 407 } else { 408 // Run validation immediately and invoke the callback with the results. 409 validator->RunValidation(); 410 callback.Run(validator.get()); 411 } 412} 413 414void UserCloudPolicyStore::StorePolicyAfterValidation( 415 UserCloudPolicyValidator* validator) { 416 validation_status_ = validator->status(); 417 DVLOG(1) << "Policy validation complete: status = " << validation_status_; 418 if (!validator->success()) { 419 status_ = STATUS_VALIDATION_ERROR; 420 NotifyStoreError(); 421 return; 422 } 423 424 // Persist the validated policy (just fire a task - don't bother getting a 425 // reply because we can't do anything if it fails). 426 background_task_runner()->PostTask( 427 FROM_HERE, 428 base::Bind(&StorePolicyToDiskOnBackgroundThread, 429 policy_path_, key_path_, verification_key_, 430 *validator->policy())); 431 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 432 433 // If the key was rotated, update our local cache of the key. 434 if (validator->policy()->has_new_public_key()) 435 policy_key_ = validator->policy()->new_public_key(); 436 status_ = STATUS_OK; 437 NotifyStoreLoaded(); 438} 439 440} // namespace policy 441