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