user_cloud_policy_store_chromeos.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/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/cloud/proto/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 : ALLOW_THIS_IN_INITIALIZER_LIST(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 ALLOW_THIS_IN_INITIALIZER_LIST(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_->RetrieveUserPolicy( 201 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved, 202 weak_factory_.GetWeakPtr())); 203} 204 205void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore( 206 scoped_ptr<em::PolicyFetchResponse> policy) { 207 // Create and configure a validator. 208 scoped_ptr<UserCloudPolicyValidator> validator = 209 CreateValidator(policy.Pass()); 210 validator->ValidateUsername(username_); 211 if (policy_key_.empty()) { 212 validator->ValidateInitialKey(); 213 } else { 214 const bool allow_rotation = true; 215 validator->ValidateSignature(policy_key_, allow_rotation); 216 } 217 218 // Start validation. The Validator will delete itself once validation is 219 // complete. 220 validator.release()->StartValidation( 221 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated, 222 weak_factory_.GetWeakPtr())); 223} 224 225void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated( 226 UserCloudPolicyValidator* validator) { 227 validation_status_ = validator->status(); 228 229 UMA_HISTOGRAM_ENUMERATION( 230 "Enterprise.UserPolicyValidationStoreStatus", 231 validation_status_, 232 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); 233 234 if (!validator->success()) { 235 status_ = STATUS_VALIDATION_ERROR; 236 NotifyStoreError(); 237 return; 238 } 239 240 std::string policy_blob; 241 if (!validator->policy()->SerializeToString(&policy_blob)) { 242 status_ = STATUS_SERIALIZE_ERROR; 243 NotifyStoreError(); 244 return; 245 } 246 247 session_manager_client_->StoreUserPolicy( 248 policy_blob, 249 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored, 250 weak_factory_.GetWeakPtr())); 251} 252 253void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) { 254 if (!success) { 255 status_ = STATUS_STORE_ERROR; 256 NotifyStoreError(); 257 } else { 258 // Load the policy right after storing it, to make sure it was accepted by 259 // the session manager. An additional validation is performed after the 260 // load; reload the key for that validation too, in case it was rotated. 261 ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load, 262 weak_factory_.GetWeakPtr())); 263 } 264} 265 266void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved( 267 const std::string& policy_blob) { 268 if (policy_blob.empty()) { 269 // Policy fetch failed. Try legacy caches if we haven't done that already. 270 if (!legacy_caches_loaded_ && legacy_loader_.get()) { 271 legacy_caches_loaded_ = true; 272 legacy_loader_->Load( 273 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished, 274 weak_factory_.GetWeakPtr())); 275 } else { 276 // session_manager doesn't have policy. Adjust internal state and notify 277 // the world about the policy update. 278 policy_.reset(); 279 NotifyStoreLoaded(); 280 } 281 return; 282 } 283 284 // Policy is supplied by session_manager. Disregard legacy data from now on. 285 legacy_loader_.reset(); 286 287 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse()); 288 if (!policy->ParseFromString(policy_blob)) { 289 status_ = STATUS_PARSE_ERROR; 290 NotifyStoreError(); 291 return; 292 } 293 294 // Load |policy_key_| to verify the loaded policy. 295 EnsurePolicyKeyLoaded( 296 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy, 297 weak_factory_.GetWeakPtr(), 298 base::Passed(&policy))); 299} 300 301void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy( 302 scoped_ptr<em::PolicyFetchResponse> policy) { 303 // Create and configure a validator for the loaded policy. 304 scoped_ptr<UserCloudPolicyValidator> validator = 305 CreateValidator(policy.Pass()); 306 validator->ValidateUsername(username_); 307 const bool allow_rotation = false; 308 validator->ValidateSignature(policy_key_, allow_rotation); 309 // Start validation. The Validator will delete itself once validation is 310 // complete. 311 validator.release()->StartValidation( 312 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated, 313 weak_factory_.GetWeakPtr())); 314} 315 316void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated( 317 UserCloudPolicyValidator* validator) { 318 validation_status_ = validator->status(); 319 320 UMA_HISTOGRAM_ENUMERATION( 321 "Enterprise.UserPolicyValidationLoadStatus", 322 validation_status_, 323 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); 324 325 if (!validator->success()) { 326 status_ = STATUS_VALIDATION_ERROR; 327 NotifyStoreError(); 328 return; 329 } 330 331 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 332 status_ = STATUS_OK; 333 334 // Policy has been loaded successfully. This indicates that new-style policy 335 // is working, so the legacy cache directory can be removed. 336 if (!legacy_cache_dir_.empty()) { 337 content::BrowserThread::PostBlockingPoolTask( 338 FROM_HERE, 339 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir, 340 legacy_cache_dir_)); 341 legacy_cache_dir_.clear(); 342 } 343 NotifyStoreLoaded(); 344} 345 346void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished( 347 const std::string& dm_token, 348 const std::string& device_id, 349 Status status, 350 scoped_ptr<em::PolicyFetchResponse> policy) { 351 status_ = status; 352 if (policy.get()) { 353 // Create and configure a validator for the loaded legacy policy. Note that 354 // the signature on this policy is not verified. 355 scoped_ptr<UserCloudPolicyValidator> validator = 356 CreateValidator(policy.Pass()); 357 validator->ValidateUsername(username_); 358 validator.release()->StartValidation( 359 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated, 360 weak_factory_.GetWeakPtr(), 361 dm_token, 362 device_id)); 363 } else { 364 InstallLegacyTokens(dm_token, device_id); 365 } 366} 367 368void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated( 369 const std::string& dm_token, 370 const std::string& device_id, 371 UserCloudPolicyValidator* validator) { 372 validation_status_ = validator->status(); 373 if (validator->success()) { 374 status_ = STATUS_OK; 375 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 376 377 // Clear the public key version. The public key version field would 378 // otherwise indicate that we have key installed in the store when in fact 379 // we haven't. This may result in policy updates failing signature 380 // verification. 381 policy_->clear_public_key_version(); 382 } else { 383 status_ = STATUS_VALIDATION_ERROR; 384 } 385 386 InstallLegacyTokens(dm_token, device_id); 387} 388 389void UserCloudPolicyStoreChromeOS::InstallLegacyTokens( 390 const std::string& dm_token, 391 const std::string& device_id) { 392 // Write token and device ID to |policy_|, giving them precedence over the 393 // policy blob. This is to match the legacy behavior, which used token and 394 // device id exclusively from the token cache file. 395 if (!dm_token.empty() && !device_id.empty()) { 396 if (!policy_.get()) 397 policy_.reset(new em::PolicyData()); 398 policy_->set_request_token(dm_token); 399 policy_->set_device_id(device_id); 400 } 401 402 // Tell the rest of the world that the policy load completed. 403 NotifyStoreLoaded(); 404} 405 406// static 407void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir( 408 const base::FilePath& dir) { 409 if (file_util::PathExists(dir) && !file_util::Delete(dir, true)) 410 LOG(ERROR) << "Failed to remove cache dir " << dir.value(); 411} 412 413void UserCloudPolicyStoreChromeOS::ReloadPolicyKey( 414 const base::Closure& callback) { 415 std::vector<uint8>* key = new std::vector<uint8>(); 416 content::BrowserThread::PostBlockingPoolTaskAndReply( 417 FROM_HERE, 418 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey, 419 policy_key_path_, 420 key), 421 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded, 422 weak_factory_.GetWeakPtr(), 423 base::Owned(key), 424 callback)); 425} 426 427// static 428void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path, 429 std::vector<uint8>* key) { 430 if (!file_util::PathExists(path)) { 431 // There is no policy key the first time that a user fetches policy. If 432 // |path| does not exist then that is the most likely scenario, so there's 433 // no need to sample a failure. 434 VLOG(1) << "No key at " << path.value(); 435 return; 436 } 437 438 int64 size; 439 if (!file_util::GetFileSize(path, &size)) { 440 LOG(ERROR) << "Could not get size of " << path.value(); 441 } else if (size == 0 || size > kKeySizeLimit) { 442 LOG(ERROR) << "Key at " << path.value() << " has bad size " << size; 443 } else { 444 key->resize(size); 445 int read_size = file_util::ReadFile( 446 path, reinterpret_cast<char*>(vector_as_array(key)), size); 447 if (read_size != size) { 448 LOG(ERROR) << "Failed to read key at " << path.value(); 449 key->clear(); 450 } 451 } 452 453 if (key->empty()) 454 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY); 455} 456 457void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded( 458 std::vector<uint8>* key, 459 const base::Closure& callback) { 460 policy_key_.swap(*key); 461 policy_key_loaded_ = true; 462 callback.Run(); 463} 464 465void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded( 466 const base::Closure& callback) { 467 if (policy_key_loaded_) { 468 callback.Run(); 469 } else { 470 // Get the hashed username that's part of the key's path, to determine 471 // |policy_key_path_|. 472 cryptohome_client_->GetSanitizedUsername(username_, 473 base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername, 474 weak_factory_.GetWeakPtr(), 475 callback)); 476 } 477} 478 479void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername( 480 const base::Closure& callback, 481 chromeos::DBusMethodCallStatus call_status, 482 const std::string& sanitized_username) { 483 // The default empty path will always yield an empty key. 484 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && 485 !sanitized_username.empty()) { 486 policy_key_path_ = user_policy_key_dir_.Append( 487 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str())); 488 } else { 489 SampleValidationFailure(VALIDATION_FAILURE_DBUS); 490 } 491 ReloadPolicyKey(callback); 492} 493 494} // namespace policy 495