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