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.
5 #include "chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.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"
26 namespace em = enterprise_management;
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");
37 // Maximum key size that will be loaded, in bytes.
38 const size_t kKeySizeLimit = 16 * 1024;
40 enum ValidationFailure {
41 VALIDATION_FAILURE_DBUS,
42 VALIDATION_FAILURE_LOAD_KEY,
43 VALIDATION_FAILURE_SIZE,
46 void SampleValidationFailure(ValidationFailure sample) {
47 UMA_HISTOGRAM_ENUMERATION("Enterprise.UserPolicyValidationFailure",
49 VALIDATION_FAILURE_SIZE);
52 // Extracts the domain name from the passed username.
53 std::string ExtractDomain(const std::string& username) {
54 return gaia::ExtractDomainName(gaia::CanonicalizeEmail(username));
59 // Helper class for loading legacy policy caches.
60 class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate,
61 public UserPolicyDiskCache::Delegate {
63 typedef base::Callback<void(const std::string&,
65 CloudPolicyStore::Status,
66 scoped_ptr<em::PolicyFetchResponse>)> Callback;
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();
74 // Starts loading, and reports the result to |callback| when done.
75 void Load(const Callback& callback);
77 // UserPolicyTokenLoader::Delegate:
78 virtual void OnTokenLoaded(const std::string& token,
79 const std::string& device_id) override;
81 // UserPolicyDiskCache::Delegate:
82 virtual void OnDiskCacheLoaded(
83 UserPolicyDiskCache::LoadResult result,
84 const em::CachedCloudPolicyResponse& policy) override;
87 // Checks whether the load operations from the legacy caches completed. If so,
88 // fires the appropriate notification.
89 void CheckLoadFinished();
91 // Maps a disk cache LoadResult to a CloudPolicyStore::Status.
92 static CloudPolicyStore::Status TranslateLoadResult(
93 UserPolicyDiskCache::LoadResult result);
95 scoped_refptr<UserPolicyTokenLoader> token_loader_;
96 scoped_refptr<UserPolicyDiskCache> policy_cache_;
98 std::string dm_token_;
99 std::string device_id_;
101 scoped_ptr<em::PolicyFetchResponse> policy_;
102 CloudPolicyStore::Status status_;
106 base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_;
108 DISALLOW_COPY_AND_ASSIGN(LegacyPolicyCacheLoader);
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(),
120 background_task_runner);
121 policy_cache_ = new UserPolicyDiskCache(weak_factory_.GetWeakPtr(),
123 background_task_runner);
126 LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {}
128 void LegacyPolicyCacheLoader::Load(const Callback& callback) {
129 callback_ = callback;
130 token_loader_->Load();
131 policy_cache_->Load();
134 void LegacyPolicyCacheLoader::OnTokenLoaded(const std::string& token,
135 const std::string& device_id) {
137 device_id_ = device_id;
138 token_loader_ = NULL;
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()));
150 LOG(WARNING) << "Failed to load legacy policy cache: " << result;
152 policy_cache_ = NULL;
156 void LegacyPolicyCacheLoader::CheckLoadFinished() {
157 if (!token_loader_.get() && !policy_cache_.get())
158 callback_.Run(dm_token_, device_id_, status_, policy_.Pass());
162 CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult(
163 UserPolicyDiskCache::LoadResult 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;
173 return CloudPolicyStore::STATUS_OK;
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),
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) {}
197 UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {}
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)));
211 void UserCloudPolicyStoreChromeOS::Load() {
212 // Cancel all pending requests.
213 weak_factory_.InvalidateWeakPtrs();
214 session_manager_client_->RetrievePolicyForUser(
216 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved,
217 weak_factory_.GetWeakPtr()));
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.
239 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
240 if (!policy->ParseFromString(policy_blob)) {
241 status_ = STATUS_PARSE_ERROR;
246 std::string sanitized_username =
247 cryptohome_client_->BlockingGetSanitizedUsername(username_);
248 if (sanitized_username.empty()) {
249 status_ = STATUS_LOAD_ERROR;
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;
259 scoped_ptr<UserCloudPolicyValidator> validator =
260 CreateValidatorForLoad(policy.Pass());
261 validator->RunValidation();
262 OnRetrievedPolicyValidated(validator.get());
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_));
276 const bool allow_rotation = true;
277 validator->ValidateSignature(policy_key_,
278 GetPolicyVerificationKey(),
279 ExtractDomain(username_),
283 // Start validation. The Validator will delete itself once validation is
285 validator.release()->StartValidation(
286 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
287 weak_factory_.GetWeakPtr()));
290 void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
291 UserCloudPolicyValidator* validator) {
292 validation_status_ = validator->status();
294 UMA_HISTOGRAM_ENUMERATION(
295 "Enterprise.UserPolicyValidationStoreStatus",
297 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
299 if (!validator->success()) {
300 status_ = STATUS_VALIDATION_ERROR;
305 std::string policy_blob;
306 if (!validator->policy()->SerializeToString(&policy_blob)) {
307 status_ = STATUS_SERIALIZE_ERROR;
312 session_manager_client_->StorePolicyForUser(
315 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
316 weak_factory_.GetWeakPtr()));
319 void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
321 status_ = STATUS_STORE_ERROR;
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()));
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()));
342 // session_manager doesn't have policy. Adjust internal state and notify
343 // the world about the policy update.
350 // Policy is supplied by session_manager. Disregard legacy data from now on.
351 legacy_loader_.reset();
353 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
354 if (!policy->ParseFromString(policy_blob)) {
355 status_ = STATUS_PARSE_ERROR;
360 // Load |policy_key_| to verify the loaded policy.
361 EnsurePolicyKeyLoaded(
362 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
363 weak_factory_.GetWeakPtr(),
364 base::Passed(&policy)));
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
374 validator.release()->StartValidation(
375 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
376 weak_factory_.GetWeakPtr()));
379 void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
380 UserCloudPolicyValidator* validator) {
381 validation_status_ = validator->status();
383 UMA_HISTOGRAM_ENUMERATION(
384 "Enterprise.UserPolicyValidationLoadStatus",
386 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
388 if (!validator->success()) {
389 status_ = STATUS_VALIDATION_ERROR;
394 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
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(
402 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
404 legacy_cache_dir_.clear();
409 void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
410 const std::string& dm_token,
411 const std::string& device_id,
413 scoped_ptr<em::PolicyFetchResponse> policy) {
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(),
428 InstallLegacyTokens(dm_token, device_id);
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()) {
439 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
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
445 policy_->clear_public_key_version();
447 status_ = STATUS_VALIDATION_ERROR;
450 InstallLegacyTokens(dm_token, device_id);
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()) {
461 policy_.reset(new em::PolicyData());
462 policy_->set_request_token(dm_token);
463 policy_->set_device_id(device_id);
466 // Tell the rest of the world that the policy load completed.
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();
477 void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
478 const base::Closure& callback) {
479 std::string* key = new std::string();
480 background_task_runner()->PostTaskAndReply(
482 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
485 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
486 weak_factory_.GetWeakPtr(),
492 void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
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();
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");
510 } else if (!read_success) {
511 LOG(ERROR) << "Failed to read key at " << path.value();
515 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
518 void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
520 const base::Closure& callback) {
522 policy_key_loaded_ = true;
526 void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
527 const base::Closure& callback) {
528 if (policy_key_loaded_) {
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(),
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()));
550 SampleValidationFailure(VALIDATION_FAILURE_DBUS);
552 ReloadPolicyKey(callback);
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();
571 } // namespace policy