user_cloud_policy_store_chromeos.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright (c) 2012 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/chromeos/policy/user_cloud_policy_store_chromeos.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/callback.h" 10#include "base/file_util.h" 11#include "base/location.h" 12#include "base/logging.h" 13#include "base/metrics/histogram.h" 14#include "base/sequenced_task_runner.h" 15#include "base/stl_util.h" 16#include "base/strings/stringprintf.h" 17#include "chrome/browser/chromeos/policy/user_policy_disk_cache.h" 18#include "chrome/browser/chromeos/policy/user_policy_token_loader.h" 19#include "chromeos/dbus/cryptohome_client.h" 20#include "chromeos/dbus/session_manager_client.h" 21#include "components/policy/core/common/cloud/cloud_policy_constants.h" 22#include "google_apis/gaia/gaia_auth_util.h" 23#include "policy/proto/cloud_policy.pb.h" 24#include "policy/proto/device_management_local.pb.h" 25 26namespace em = enterprise_management; 27 28namespace policy { 29 30namespace { 31 32// Path within |user_policy_key_dir_| that contains the policy key. 33// "%s" must be substituted with the sanitized username. 34const base::FilePath::CharType kPolicyKeyFile[] = 35 FILE_PATH_LITERAL("%s/policy.pub"); 36 37// Maximum key size that will be loaded, in bytes. 38const size_t kKeySizeLimit = 16 * 1024; 39 40enum ValidationFailure { 41 VALIDATION_FAILURE_DBUS, 42 VALIDATION_FAILURE_LOAD_KEY, 43 VALIDATION_FAILURE_SIZE, 44}; 45 46void SampleValidationFailure(ValidationFailure sample) { 47 UMA_HISTOGRAM_ENUMERATION("Enterprise.UserPolicyValidationFailure", 48 sample, 49 VALIDATION_FAILURE_SIZE); 50} 51 52// Extracts the domain name from the passed username. 53std::string ExtractDomain(const std::string& username) { 54 return gaia::ExtractDomainName(gaia::CanonicalizeEmail(username)); 55} 56 57} // namespace 58 59// Helper class for loading legacy policy caches. 60class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate, 61 public UserPolicyDiskCache::Delegate { 62 public: 63 typedef base::Callback<void(const std::string&, 64 const std::string&, 65 CloudPolicyStore::Status, 66 scoped_ptr<em::PolicyFetchResponse>)> Callback; 67 68 LegacyPolicyCacheLoader( 69 const base::FilePath& token_cache_file, 70 const base::FilePath& policy_cache_file, 71 scoped_refptr<base::SequencedTaskRunner> background_task_runner); 72 virtual ~LegacyPolicyCacheLoader(); 73 74 // Starts loading, and reports the result to |callback| when done. 75 void Load(const Callback& callback); 76 77 // UserPolicyTokenLoader::Delegate: 78 virtual void OnTokenLoaded(const std::string& token, 79 const std::string& device_id) OVERRIDE; 80 81 // UserPolicyDiskCache::Delegate: 82 virtual void OnDiskCacheLoaded( 83 UserPolicyDiskCache::LoadResult result, 84 const em::CachedCloudPolicyResponse& policy) OVERRIDE; 85 86 private: 87 // Checks whether the load operations from the legacy caches completed. If so, 88 // fires the appropriate notification. 89 void CheckLoadFinished(); 90 91 // Maps a disk cache LoadResult to a CloudPolicyStore::Status. 92 static CloudPolicyStore::Status TranslateLoadResult( 93 UserPolicyDiskCache::LoadResult result); 94 95 base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_; 96 97 scoped_refptr<UserPolicyTokenLoader> token_loader_; 98 scoped_refptr<UserPolicyDiskCache> policy_cache_; 99 100 std::string dm_token_; 101 std::string device_id_; 102 bool has_policy_; 103 scoped_ptr<em::PolicyFetchResponse> policy_; 104 CloudPolicyStore::Status status_; 105 106 Callback callback_; 107 108 DISALLOW_COPY_AND_ASSIGN(LegacyPolicyCacheLoader); 109}; 110 111LegacyPolicyCacheLoader::LegacyPolicyCacheLoader( 112 const base::FilePath& token_cache_file, 113 const base::FilePath& policy_cache_file, 114 scoped_refptr<base::SequencedTaskRunner> background_task_runner) 115 : weak_factory_(this), 116 has_policy_(false), 117 status_(CloudPolicyStore::STATUS_OK) { 118 token_loader_ = new UserPolicyTokenLoader(weak_factory_.GetWeakPtr(), 119 token_cache_file, 120 background_task_runner); 121 policy_cache_ = new UserPolicyDiskCache(weak_factory_.GetWeakPtr(), 122 policy_cache_file, 123 background_task_runner); 124} 125 126LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {} 127 128void LegacyPolicyCacheLoader::Load(const Callback& callback) { 129 callback_ = callback; 130 token_loader_->Load(); 131 policy_cache_->Load(); 132} 133 134void LegacyPolicyCacheLoader::OnTokenLoaded(const std::string& token, 135 const std::string& device_id) { 136 dm_token_ = token; 137 device_id_ = device_id; 138 token_loader_ = NULL; 139 CheckLoadFinished(); 140} 141 142void LegacyPolicyCacheLoader::OnDiskCacheLoaded( 143 UserPolicyDiskCache::LoadResult result, 144 const em::CachedCloudPolicyResponse& policy) { 145 status_ = TranslateLoadResult(result); 146 if (result == UserPolicyDiskCache::LOAD_RESULT_SUCCESS) { 147 if (policy.has_cloud_policy()) 148 policy_.reset(new em::PolicyFetchResponse(policy.cloud_policy())); 149 } else { 150 LOG(WARNING) << "Failed to load legacy policy cache: " << result; 151 } 152 policy_cache_ = NULL; 153 CheckLoadFinished(); 154} 155 156void LegacyPolicyCacheLoader::CheckLoadFinished() { 157 if (!token_loader_.get() && !policy_cache_.get()) 158 callback_.Run(dm_token_, device_id_, status_, policy_.Pass()); 159} 160 161// static 162CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult( 163 UserPolicyDiskCache::LoadResult result) { 164 switch (result) { 165 case UserPolicyDiskCache::LOAD_RESULT_SUCCESS: 166 case UserPolicyDiskCache::LOAD_RESULT_NOT_FOUND: 167 return CloudPolicyStore::STATUS_OK; 168 case UserPolicyDiskCache::LOAD_RESULT_PARSE_ERROR: 169 case UserPolicyDiskCache::LOAD_RESULT_READ_ERROR: 170 return CloudPolicyStore::STATUS_LOAD_ERROR; 171 } 172 NOTREACHED(); 173 return CloudPolicyStore::STATUS_OK; 174} 175 176UserCloudPolicyStoreChromeOS::UserCloudPolicyStoreChromeOS( 177 chromeos::CryptohomeClient* cryptohome_client, 178 chromeos::SessionManagerClient* session_manager_client, 179 scoped_refptr<base::SequencedTaskRunner> background_task_runner, 180 const std::string& username, 181 const base::FilePath& user_policy_key_dir, 182 const base::FilePath& legacy_token_cache_file, 183 const base::FilePath& legacy_policy_cache_file) 184 : UserCloudPolicyStoreBase(background_task_runner), 185 cryptohome_client_(cryptohome_client), 186 session_manager_client_(session_manager_client), 187 username_(username), 188 user_policy_key_dir_(user_policy_key_dir), 189 weak_factory_(this), 190 legacy_cache_dir_(legacy_token_cache_file.DirName()), 191 legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file, 192 legacy_policy_cache_file, 193 background_task_runner)), 194 legacy_caches_loaded_(false), 195 policy_key_loaded_(false) {} 196 197UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {} 198 199void UserCloudPolicyStoreChromeOS::Store( 200 const em::PolicyFetchResponse& policy) { 201 // Cancel all pending requests. 202 weak_factory_.InvalidateWeakPtrs(); 203 scoped_ptr<em::PolicyFetchResponse> response( 204 new em::PolicyFetchResponse(policy)); 205 EnsurePolicyKeyLoaded( 206 base::Bind(&UserCloudPolicyStoreChromeOS::ValidatePolicyForStore, 207 weak_factory_.GetWeakPtr(), 208 base::Passed(&response))); 209} 210 211void UserCloudPolicyStoreChromeOS::Load() { 212 // Cancel all pending requests. 213 weak_factory_.InvalidateWeakPtrs(); 214 session_manager_client_->RetrievePolicyForUser( 215 username_, 216 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved, 217 weak_factory_.GetWeakPtr())); 218} 219 220void UserCloudPolicyStoreChromeOS::LoadImmediately() { 221 // This blocking DBus call is in the startup path and will block the UI 222 // thread. This only happens when the Profile is created synchronously, which 223 // on ChromeOS happens whenever the browser is restarted into the same 224 // session. That happens when the browser crashes, or right after signin if 225 // the user has flags configured in about:flags. 226 // However, on those paths we must load policy synchronously so that the 227 // Profile initialization never sees unmanaged prefs, which would lead to 228 // data loss. http://crbug.com/263061 229 std::string policy_blob = 230 session_manager_client_->BlockingRetrievePolicyForUser(username_); 231 if (policy_blob.empty()) { 232 // The session manager doesn't have policy, or the call failed. 233 // Just notify that the load is done, and don't bother with the legacy 234 // caches in this case. 235 NotifyStoreLoaded(); 236 return; 237 } 238 239 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse()); 240 if (!policy->ParseFromString(policy_blob)) { 241 status_ = STATUS_PARSE_ERROR; 242 NotifyStoreError(); 243 return; 244 } 245 246 std::string sanitized_username = 247 cryptohome_client_->BlockingGetSanitizedUsername(username_); 248 if (sanitized_username.empty()) { 249 status_ = STATUS_LOAD_ERROR; 250 NotifyStoreError(); 251 return; 252 } 253 254 policy_key_path_ = user_policy_key_dir_.Append( 255 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str())); 256 LoadPolicyKey(policy_key_path_, &policy_key_); 257 policy_key_loaded_ = true; 258 259 scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator( 260 policy.Pass(), CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE); 261 validator->ValidateUsername(username_, true); 262 const bool allow_rotation = false; 263 validator->ValidateSignature( 264 policy_key_, 265 GetPolicyVerificationKey(), 266 ExtractDomain(sanitized_username), 267 allow_rotation); 268 validator->RunValidation(); 269 OnRetrievedPolicyValidated(validator.get()); 270} 271 272void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore( 273 scoped_ptr<em::PolicyFetchResponse> policy) { 274 // Create and configure a validator. 275 scoped_ptr<UserCloudPolicyValidator> validator = 276 CreateValidator(policy.Pass(), 277 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); 278 validator->ValidateUsername(username_, true); 279 if (policy_key_.empty()) { 280 validator->ValidateInitialKey(GetPolicyVerificationKey(), 281 ExtractDomain(username_)); 282 } else { 283 const bool allow_rotation = true; 284 validator->ValidateSignature(policy_key_, 285 GetPolicyVerificationKey(), 286 ExtractDomain(username_), 287 allow_rotation); 288 } 289 290 // Start validation. The Validator will delete itself once validation is 291 // complete. 292 validator.release()->StartValidation( 293 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated, 294 weak_factory_.GetWeakPtr())); 295} 296 297void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated( 298 UserCloudPolicyValidator* validator) { 299 validation_status_ = validator->status(); 300 301 UMA_HISTOGRAM_ENUMERATION( 302 "Enterprise.UserPolicyValidationStoreStatus", 303 validation_status_, 304 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE); 305 306 if (!validator->success()) { 307 status_ = STATUS_VALIDATION_ERROR; 308 NotifyStoreError(); 309 return; 310 } 311 312 std::string policy_blob; 313 if (!validator->policy()->SerializeToString(&policy_blob)) { 314 status_ = STATUS_SERIALIZE_ERROR; 315 NotifyStoreError(); 316 return; 317 } 318 319 session_manager_client_->StorePolicyForUser( 320 username_, 321 policy_blob, 322 validator->policy()->new_public_key(), 323 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored, 324 weak_factory_.GetWeakPtr())); 325} 326 327void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) { 328 if (!success) { 329 status_ = STATUS_STORE_ERROR; 330 NotifyStoreError(); 331 } else { 332 // Load the policy right after storing it, to make sure it was accepted by 333 // the session manager. An additional validation is performed after the 334 // load; reload the key for that validation too, in case it was rotated. 335 ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load, 336 weak_factory_.GetWeakPtr())); 337 } 338} 339 340void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved( 341 const std::string& policy_blob) { 342 if (policy_blob.empty()) { 343 // Policy fetch failed. Try legacy caches if we haven't done that already. 344 if (!legacy_caches_loaded_ && legacy_loader_.get()) { 345 legacy_caches_loaded_ = true; 346 legacy_loader_->Load( 347 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished, 348 weak_factory_.GetWeakPtr())); 349 } else { 350 // session_manager doesn't have policy. Adjust internal state and notify 351 // the world about the policy update. 352 policy_.reset(); 353 NotifyStoreLoaded(); 354 } 355 return; 356 } 357 358 // Policy is supplied by session_manager. Disregard legacy data from now on. 359 legacy_loader_.reset(); 360 361 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse()); 362 if (!policy->ParseFromString(policy_blob)) { 363 status_ = STATUS_PARSE_ERROR; 364 NotifyStoreError(); 365 return; 366 } 367 368 // Load |policy_key_| to verify the loaded policy. 369 EnsurePolicyKeyLoaded( 370 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy, 371 weak_factory_.GetWeakPtr(), 372 base::Passed(&policy))); 373} 374 375void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy( 376 scoped_ptr<em::PolicyFetchResponse> policy) { 377 // Create and configure a validator for the loaded policy. 378 scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator( 379 policy.Pass(), CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE); 380 validator->ValidateUsername(username_, true); 381 const bool allow_rotation = false; 382 validator->ValidateSignature(policy_key_, 383 GetPolicyVerificationKey(), 384 ExtractDomain(username_), 385 allow_rotation); 386 // Start validation. The Validator will delete itself once validation is 387 // complete. 388 validator.release()->StartValidation( 389 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated, 390 weak_factory_.GetWeakPtr())); 391} 392 393void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated( 394 UserCloudPolicyValidator* validator) { 395 validation_status_ = validator->status(); 396 397 UMA_HISTOGRAM_ENUMERATION( 398 "Enterprise.UserPolicyValidationLoadStatus", 399 validation_status_, 400 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE); 401 402 if (!validator->success()) { 403 status_ = STATUS_VALIDATION_ERROR; 404 NotifyStoreError(); 405 return; 406 } 407 408 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 409 status_ = STATUS_OK; 410 411 // Policy has been loaded successfully. This indicates that new-style policy 412 // is working, so the legacy cache directory can be removed. 413 if (!legacy_cache_dir_.empty()) { 414 background_task_runner()->PostTask( 415 FROM_HERE, 416 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir, 417 legacy_cache_dir_)); 418 legacy_cache_dir_.clear(); 419 } 420 NotifyStoreLoaded(); 421} 422 423void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished( 424 const std::string& dm_token, 425 const std::string& device_id, 426 Status status, 427 scoped_ptr<em::PolicyFetchResponse> policy) { 428 status_ = status; 429 if (policy.get()) { 430 // Create and configure a validator for the loaded legacy policy. Note that 431 // the signature on this policy is not verified. 432 scoped_ptr<UserCloudPolicyValidator> validator = 433 CreateValidator(policy.Pass(), 434 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); 435 validator->ValidateUsername(username_, true); 436 validator.release()->StartValidation( 437 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated, 438 weak_factory_.GetWeakPtr(), 439 dm_token, 440 device_id)); 441 } else { 442 InstallLegacyTokens(dm_token, device_id); 443 } 444} 445 446void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated( 447 const std::string& dm_token, 448 const std::string& device_id, 449 UserCloudPolicyValidator* validator) { 450 validation_status_ = validator->status(); 451 if (validator->success()) { 452 status_ = STATUS_OK; 453 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 454 455 // Clear the public key version. The public key version field would 456 // otherwise indicate that we have key installed in the store when in fact 457 // we haven't. This may result in policy updates failing signature 458 // verification. 459 policy_->clear_public_key_version(); 460 } else { 461 status_ = STATUS_VALIDATION_ERROR; 462 } 463 464 InstallLegacyTokens(dm_token, device_id); 465} 466 467void UserCloudPolicyStoreChromeOS::InstallLegacyTokens( 468 const std::string& dm_token, 469 const std::string& device_id) { 470 // Write token and device ID to |policy_|, giving them precedence over the 471 // policy blob. This is to match the legacy behavior, which used token and 472 // device id exclusively from the token cache file. 473 if (!dm_token.empty() && !device_id.empty()) { 474 if (!policy_.get()) 475 policy_.reset(new em::PolicyData()); 476 policy_->set_request_token(dm_token); 477 policy_->set_device_id(device_id); 478 } 479 480 // Tell the rest of the world that the policy load completed. 481 NotifyStoreLoaded(); 482} 483 484// static 485void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir( 486 const base::FilePath& dir) { 487 if (base::PathExists(dir) && !base::DeleteFile(dir, true)) 488 LOG(ERROR) << "Failed to remove cache dir " << dir.value(); 489} 490 491void UserCloudPolicyStoreChromeOS::ReloadPolicyKey( 492 const base::Closure& callback) { 493 std::string* key = new std::string(); 494 background_task_runner()->PostTaskAndReply( 495 FROM_HERE, 496 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey, 497 policy_key_path_, 498 key), 499 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded, 500 weak_factory_.GetWeakPtr(), 501 base::Owned(key), 502 callback)); 503} 504 505// static 506void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path, 507 std::string* key) { 508 if (!base::PathExists(path)) { 509 // There is no policy key the first time that a user fetches policy. If 510 // |path| does not exist then that is the most likely scenario, so there's 511 // no need to sample a failure. 512 VLOG(1) << "No key at " << path.value(); 513 return; 514 } 515 516 const bool read_success = base::ReadFileToString(path, key, kKeySizeLimit); 517 // If the read was successful and the file size is 0 or if the read fails 518 // due to file size exceeding |kKeySizeLimit|, log error. 519 if ((read_success && key->length() == 0) || 520 (!read_success && key->length() == kKeySizeLimit)) { 521 LOG(ERROR) << "Key at " << path.value() 522 << (read_success ? " is empty." : " exceeds size limit"); 523 key->clear(); 524 } else if (!read_success) { 525 LOG(ERROR) << "Failed to read key at " << path.value(); 526 } 527 528 if (key->empty()) 529 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY); 530} 531 532void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded( 533 std::string* key, 534 const base::Closure& callback) { 535 policy_key_ = *key; 536 policy_key_loaded_ = true; 537 callback.Run(); 538} 539 540void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded( 541 const base::Closure& callback) { 542 if (policy_key_loaded_) { 543 callback.Run(); 544 } else { 545 // Get the hashed username that's part of the key's path, to determine 546 // |policy_key_path_|. 547 cryptohome_client_->GetSanitizedUsername(username_, 548 base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername, 549 weak_factory_.GetWeakPtr(), 550 callback)); 551 } 552} 553 554void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername( 555 const base::Closure& callback, 556 chromeos::DBusMethodCallStatus call_status, 557 const std::string& sanitized_username) { 558 // The default empty path will always yield an empty key. 559 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && 560 !sanitized_username.empty()) { 561 policy_key_path_ = user_policy_key_dir_.Append( 562 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str())); 563 } else { 564 SampleValidationFailure(VALIDATION_FAILURE_DBUS); 565 } 566 ReloadPolicyKey(callback); 567} 568 569} // namespace policy 570