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