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