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 25 namespace em = enterprise_management; 26 27 namespace policy { 28 29 namespace { 30 31 // Path within |user_policy_key_dir_| that contains the policy key. 32 // "%s" must be substituted with the sanitized username. 33 const base::FilePath::CharType kPolicyKeyFile[] = 34 FILE_PATH_LITERAL("%s/policy.pub"); 35 36 // Maximum key size that will be loaded, in bytes. 37 const int kKeySizeLimit = 16 * 1024; 38 39 enum ValidationFailure { 40 VALIDATION_FAILURE_DBUS, 41 VALIDATION_FAILURE_LOAD_KEY, 42 VALIDATION_FAILURE_SIZE, 43 }; 44 45 void 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. 54 class 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 103 LegacyPolicyCacheLoader::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 115 LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {} 116 117 void LegacyPolicyCacheLoader::Load(const Callback& callback) { 118 callback_ = callback; 119 token_loader_->Load(); 120 policy_cache_->Load(); 121 } 122 123 void 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 131 void 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 145 void LegacyPolicyCacheLoader::CheckLoadFinished() { 146 if (!token_loader_.get() && !policy_cache_.get()) 147 callback_.Run(dm_token_, device_id_, status_, policy_.Pass()); 148 } 149 150 // static 151 CloudPolicyStore::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 165 UserCloudPolicyStoreChromeOS::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 183 UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {} 184 185 void 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 197 void 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 206 void 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 validator->ValidateUsername(username_); 248 const bool allow_rotation = false; 249 validator->ValidateSignature(policy_key_, allow_rotation); 250 validator->RunValidation(); 251 OnRetrievedPolicyValidated(validator.get()); 252 } 253 254 void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore( 255 scoped_ptr<em::PolicyFetchResponse> policy) { 256 // Create and configure a validator. 257 scoped_ptr<UserCloudPolicyValidator> validator = 258 CreateValidator(policy.Pass()); 259 validator->ValidateUsername(username_); 260 if (policy_key_.empty()) { 261 validator->ValidateInitialKey(); 262 } else { 263 const bool allow_rotation = true; 264 validator->ValidateSignature(policy_key_, allow_rotation); 265 } 266 267 // Start validation. The Validator will delete itself once validation is 268 // complete. 269 validator.release()->StartValidation( 270 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated, 271 weak_factory_.GetWeakPtr())); 272 } 273 274 void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated( 275 UserCloudPolicyValidator* validator) { 276 validation_status_ = validator->status(); 277 278 UMA_HISTOGRAM_ENUMERATION( 279 "Enterprise.UserPolicyValidationStoreStatus", 280 validation_status_, 281 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); 282 283 if (!validator->success()) { 284 status_ = STATUS_VALIDATION_ERROR; 285 NotifyStoreError(); 286 return; 287 } 288 289 std::string policy_blob; 290 if (!validator->policy()->SerializeToString(&policy_blob)) { 291 status_ = STATUS_SERIALIZE_ERROR; 292 NotifyStoreError(); 293 return; 294 } 295 296 session_manager_client_->StorePolicyForUser( 297 username_, 298 policy_blob, 299 validator->policy()->new_public_key(), 300 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored, 301 weak_factory_.GetWeakPtr())); 302 } 303 304 void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) { 305 if (!success) { 306 status_ = STATUS_STORE_ERROR; 307 NotifyStoreError(); 308 } else { 309 // Load the policy right after storing it, to make sure it was accepted by 310 // the session manager. An additional validation is performed after the 311 // load; reload the key for that validation too, in case it was rotated. 312 ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load, 313 weak_factory_.GetWeakPtr())); 314 } 315 } 316 317 void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved( 318 const std::string& policy_blob) { 319 if (policy_blob.empty()) { 320 // Policy fetch failed. Try legacy caches if we haven't done that already. 321 if (!legacy_caches_loaded_ && legacy_loader_.get()) { 322 legacy_caches_loaded_ = true; 323 legacy_loader_->Load( 324 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished, 325 weak_factory_.GetWeakPtr())); 326 } else { 327 // session_manager doesn't have policy. Adjust internal state and notify 328 // the world about the policy update. 329 policy_.reset(); 330 NotifyStoreLoaded(); 331 } 332 return; 333 } 334 335 // Policy is supplied by session_manager. Disregard legacy data from now on. 336 legacy_loader_.reset(); 337 338 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse()); 339 if (!policy->ParseFromString(policy_blob)) { 340 status_ = STATUS_PARSE_ERROR; 341 NotifyStoreError(); 342 return; 343 } 344 345 // Load |policy_key_| to verify the loaded policy. 346 EnsurePolicyKeyLoaded( 347 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy, 348 weak_factory_.GetWeakPtr(), 349 base::Passed(&policy))); 350 } 351 352 void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy( 353 scoped_ptr<em::PolicyFetchResponse> policy) { 354 // Create and configure a validator for the loaded policy. 355 scoped_ptr<UserCloudPolicyValidator> validator = 356 CreateValidator(policy.Pass()); 357 validator->ValidateUsername(username_); 358 const bool allow_rotation = false; 359 validator->ValidateSignature(policy_key_, allow_rotation); 360 // Start validation. The Validator will delete itself once validation is 361 // complete. 362 validator.release()->StartValidation( 363 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated, 364 weak_factory_.GetWeakPtr())); 365 } 366 367 void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated( 368 UserCloudPolicyValidator* validator) { 369 validation_status_ = validator->status(); 370 371 UMA_HISTOGRAM_ENUMERATION( 372 "Enterprise.UserPolicyValidationLoadStatus", 373 validation_status_, 374 UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1); 375 376 if (!validator->success()) { 377 status_ = STATUS_VALIDATION_ERROR; 378 NotifyStoreError(); 379 return; 380 } 381 382 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 383 status_ = STATUS_OK; 384 385 // Policy has been loaded successfully. This indicates that new-style policy 386 // is working, so the legacy cache directory can be removed. 387 if (!legacy_cache_dir_.empty()) { 388 content::BrowserThread::PostBlockingPoolTask( 389 FROM_HERE, 390 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir, 391 legacy_cache_dir_)); 392 legacy_cache_dir_.clear(); 393 } 394 NotifyStoreLoaded(); 395 } 396 397 void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished( 398 const std::string& dm_token, 399 const std::string& device_id, 400 Status status, 401 scoped_ptr<em::PolicyFetchResponse> policy) { 402 status_ = status; 403 if (policy.get()) { 404 // Create and configure a validator for the loaded legacy policy. Note that 405 // the signature on this policy is not verified. 406 scoped_ptr<UserCloudPolicyValidator> validator = 407 CreateValidator(policy.Pass()); 408 validator->ValidateUsername(username_); 409 validator.release()->StartValidation( 410 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated, 411 weak_factory_.GetWeakPtr(), 412 dm_token, 413 device_id)); 414 } else { 415 InstallLegacyTokens(dm_token, device_id); 416 } 417 } 418 419 void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated( 420 const std::string& dm_token, 421 const std::string& device_id, 422 UserCloudPolicyValidator* validator) { 423 validation_status_ = validator->status(); 424 if (validator->success()) { 425 status_ = STATUS_OK; 426 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass()); 427 428 // Clear the public key version. The public key version field would 429 // otherwise indicate that we have key installed in the store when in fact 430 // we haven't. This may result in policy updates failing signature 431 // verification. 432 policy_->clear_public_key_version(); 433 } else { 434 status_ = STATUS_VALIDATION_ERROR; 435 } 436 437 InstallLegacyTokens(dm_token, device_id); 438 } 439 440 void UserCloudPolicyStoreChromeOS::InstallLegacyTokens( 441 const std::string& dm_token, 442 const std::string& device_id) { 443 // Write token and device ID to |policy_|, giving them precedence over the 444 // policy blob. This is to match the legacy behavior, which used token and 445 // device id exclusively from the token cache file. 446 if (!dm_token.empty() && !device_id.empty()) { 447 if (!policy_.get()) 448 policy_.reset(new em::PolicyData()); 449 policy_->set_request_token(dm_token); 450 policy_->set_device_id(device_id); 451 } 452 453 // Tell the rest of the world that the policy load completed. 454 NotifyStoreLoaded(); 455 } 456 457 // static 458 void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir( 459 const base::FilePath& dir) { 460 if (base::PathExists(dir) && !base::DeleteFile(dir, true)) 461 LOG(ERROR) << "Failed to remove cache dir " << dir.value(); 462 } 463 464 void UserCloudPolicyStoreChromeOS::ReloadPolicyKey( 465 const base::Closure& callback) { 466 std::vector<uint8>* key = new std::vector<uint8>(); 467 content::BrowserThread::PostBlockingPoolTaskAndReply( 468 FROM_HERE, 469 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey, 470 policy_key_path_, 471 key), 472 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded, 473 weak_factory_.GetWeakPtr(), 474 base::Owned(key), 475 callback)); 476 } 477 478 // static 479 void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path, 480 std::vector<uint8>* key) { 481 if (!base::PathExists(path)) { 482 // There is no policy key the first time that a user fetches policy. If 483 // |path| does not exist then that is the most likely scenario, so there's 484 // no need to sample a failure. 485 VLOG(1) << "No key at " << path.value(); 486 return; 487 } 488 489 int64 size; 490 if (!file_util::GetFileSize(path, &size)) { 491 LOG(ERROR) << "Could not get size of " << path.value(); 492 } else if (size == 0 || size > kKeySizeLimit) { 493 LOG(ERROR) << "Key at " << path.value() << " has bad size " << size; 494 } else { 495 key->resize(size); 496 int read_size = file_util::ReadFile( 497 path, reinterpret_cast<char*>(vector_as_array(key)), size); 498 if (read_size != size) { 499 LOG(ERROR) << "Failed to read key at " << path.value(); 500 key->clear(); 501 } 502 } 503 504 if (key->empty()) 505 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY); 506 } 507 508 void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded( 509 std::vector<uint8>* key, 510 const base::Closure& callback) { 511 policy_key_.swap(*key); 512 policy_key_loaded_ = true; 513 callback.Run(); 514 } 515 516 void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded( 517 const base::Closure& callback) { 518 if (policy_key_loaded_) { 519 callback.Run(); 520 } else { 521 // Get the hashed username that's part of the key's path, to determine 522 // |policy_key_path_|. 523 cryptohome_client_->GetSanitizedUsername(username_, 524 base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername, 525 weak_factory_.GetWeakPtr(), 526 callback)); 527 } 528 } 529 530 void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername( 531 const base::Closure& callback, 532 chromeos::DBusMethodCallStatus call_status, 533 const std::string& sanitized_username) { 534 // The default empty path will always yield an empty key. 535 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS && 536 !sanitized_username.empty()) { 537 policy_key_path_ = user_policy_key_dir_.Append( 538 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str())); 539 } else { 540 SampleValidationFailure(VALIDATION_FAILURE_DBUS); 541 } 542 ReloadPolicyKey(callback); 543 } 544 545 } // namespace policy 546