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/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 int 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 base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_;
97 scoped_refptr<UserPolicyTokenLoader> token_loader_;
98 scoped_refptr<UserPolicyDiskCache> policy_cache_;
100 std::string dm_token_;
101 std::string device_id_;
103 scoped_ptr<em::PolicyFetchResponse> policy_;
104 CloudPolicyStore::Status status_;
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 : weak_factory_(this),
117 status_(CloudPolicyStore::STATUS_OK) {
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),
190 legacy_cache_dir_(legacy_token_cache_file.DirName()),
191 legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file,
192 legacy_policy_cache_file,
193 background_task_runner)),
194 legacy_caches_loaded_(false),
195 policy_key_loaded_(false) {}
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 CreateValidator(policy.Pass(),
261 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
262 validator->ValidateUsername(username_);
263 const bool allow_rotation = false;
264 validator->ValidateSignature(
266 GetPolicyVerificationKey(),
267 ExtractDomain(sanitized_username),
269 validator->RunValidation();
270 OnRetrievedPolicyValidated(validator.get());
273 void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
274 scoped_ptr<em::PolicyFetchResponse> policy) {
275 // Create and configure a validator.
276 scoped_ptr<UserCloudPolicyValidator> validator =
277 CreateValidator(policy.Pass(),
278 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
279 validator->ValidateUsername(username_);
280 if (policy_key_.empty()) {
281 validator->ValidateInitialKey(GetPolicyVerificationKey(),
282 ExtractDomain(username_));
284 const bool allow_rotation = true;
285 validator->ValidateSignature(policy_key_,
286 GetPolicyVerificationKey(),
287 ExtractDomain(username_),
291 // Start validation. The Validator will delete itself once validation is
293 validator.release()->StartValidation(
294 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
295 weak_factory_.GetWeakPtr()));
298 void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
299 UserCloudPolicyValidator* validator) {
300 validation_status_ = validator->status();
302 UMA_HISTOGRAM_ENUMERATION(
303 "Enterprise.UserPolicyValidationStoreStatus",
305 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
307 if (!validator->success()) {
308 status_ = STATUS_VALIDATION_ERROR;
313 std::string policy_blob;
314 if (!validator->policy()->SerializeToString(&policy_blob)) {
315 status_ = STATUS_SERIALIZE_ERROR;
320 session_manager_client_->StorePolicyForUser(
323 validator->policy()->new_public_key(),
324 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
325 weak_factory_.GetWeakPtr()));
328 void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
330 status_ = STATUS_STORE_ERROR;
333 // Load the policy right after storing it, to make sure it was accepted by
334 // the session manager. An additional validation is performed after the
335 // load; reload the key for that validation too, in case it was rotated.
336 ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
337 weak_factory_.GetWeakPtr()));
341 void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
342 const std::string& policy_blob) {
343 if (policy_blob.empty()) {
344 // Policy fetch failed. Try legacy caches if we haven't done that already.
345 if (!legacy_caches_loaded_ && legacy_loader_.get()) {
346 legacy_caches_loaded_ = true;
347 legacy_loader_->Load(
348 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished,
349 weak_factory_.GetWeakPtr()));
351 // session_manager doesn't have policy. Adjust internal state and notify
352 // the world about the policy update.
359 // Policy is supplied by session_manager. Disregard legacy data from now on.
360 legacy_loader_.reset();
362 scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
363 if (!policy->ParseFromString(policy_blob)) {
364 status_ = STATUS_PARSE_ERROR;
369 // Load |policy_key_| to verify the loaded policy.
370 EnsurePolicyKeyLoaded(
371 base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
372 weak_factory_.GetWeakPtr(),
373 base::Passed(&policy)));
376 void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
377 scoped_ptr<em::PolicyFetchResponse> policy) {
378 // Create and configure a validator for the loaded policy.
379 scoped_ptr<UserCloudPolicyValidator> validator =
380 CreateValidator(policy.Pass(),
381 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
382 validator->ValidateUsername(username_);
383 const bool allow_rotation = false;
384 validator->ValidateSignature(policy_key_,
385 GetPolicyVerificationKey(),
386 ExtractDomain(username_),
388 // Start validation. The Validator will delete itself once validation is
390 validator.release()->StartValidation(
391 base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
392 weak_factory_.GetWeakPtr()));
395 void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
396 UserCloudPolicyValidator* validator) {
397 validation_status_ = validator->status();
399 UMA_HISTOGRAM_ENUMERATION(
400 "Enterprise.UserPolicyValidationLoadStatus",
402 UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
404 if (!validator->success()) {
405 status_ = STATUS_VALIDATION_ERROR;
410 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
413 // Policy has been loaded successfully. This indicates that new-style policy
414 // is working, so the legacy cache directory can be removed.
415 if (!legacy_cache_dir_.empty()) {
416 background_task_runner()->PostTask(
418 base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
420 legacy_cache_dir_.clear();
425 void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
426 const std::string& dm_token,
427 const std::string& device_id,
429 scoped_ptr<em::PolicyFetchResponse> policy) {
432 // Create and configure a validator for the loaded legacy policy. Note that
433 // the signature on this policy is not verified.
434 scoped_ptr<UserCloudPolicyValidator> validator =
435 CreateValidator(policy.Pass(),
436 CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
437 validator->ValidateUsername(username_);
438 validator.release()->StartValidation(
439 base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
440 weak_factory_.GetWeakPtr(),
444 InstallLegacyTokens(dm_token, device_id);
448 void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
449 const std::string& dm_token,
450 const std::string& device_id,
451 UserCloudPolicyValidator* validator) {
452 validation_status_ = validator->status();
453 if (validator->success()) {
455 InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
457 // Clear the public key version. The public key version field would
458 // otherwise indicate that we have key installed in the store when in fact
459 // we haven't. This may result in policy updates failing signature
461 policy_->clear_public_key_version();
463 status_ = STATUS_VALIDATION_ERROR;
466 InstallLegacyTokens(dm_token, device_id);
469 void UserCloudPolicyStoreChromeOS::InstallLegacyTokens(
470 const std::string& dm_token,
471 const std::string& device_id) {
472 // Write token and device ID to |policy_|, giving them precedence over the
473 // policy blob. This is to match the legacy behavior, which used token and
474 // device id exclusively from the token cache file.
475 if (!dm_token.empty() && !device_id.empty()) {
477 policy_.reset(new em::PolicyData());
478 policy_->set_request_token(dm_token);
479 policy_->set_device_id(device_id);
482 // Tell the rest of the world that the policy load completed.
487 void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
488 const base::FilePath& dir) {
489 if (base::PathExists(dir) && !base::DeleteFile(dir, true))
490 LOG(ERROR) << "Failed to remove cache dir " << dir.value();
493 void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
494 const base::Closure& callback) {
495 std::string* key = new std::string();
496 background_task_runner()->PostTaskAndReply(
498 base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
501 base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
502 weak_factory_.GetWeakPtr(),
508 void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
510 if (!base::PathExists(path)) {
511 // There is no policy key the first time that a user fetches policy. If
512 // |path| does not exist then that is the most likely scenario, so there's
513 // no need to sample a failure.
514 VLOG(1) << "No key at " << path.value();
520 if (!base::GetFileSize(path, &size)) {
521 LOG(ERROR) << "Could not get size of " << path.value();
522 } else if (size == 0 || size > kKeySizeLimit) {
523 LOG(ERROR) << "Key at " << path.value() << " has bad size " << size;
526 int read_size = base::ReadFile(path, buf, size);
527 if (read_size != size) {
528 LOG(ERROR) << "Failed to read key at " << path.value();
530 key->append(buf, size);
535 SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
538 void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
540 const base::Closure& callback) {
542 policy_key_loaded_ = true;
546 void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
547 const base::Closure& callback) {
548 if (policy_key_loaded_) {
551 // Get the hashed username that's part of the key's path, to determine
552 // |policy_key_path_|.
553 cryptohome_client_->GetSanitizedUsername(username_,
554 base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
555 weak_factory_.GetWeakPtr(),
560 void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
561 const base::Closure& callback,
562 chromeos::DBusMethodCallStatus call_status,
563 const std::string& sanitized_username) {
564 // The default empty path will always yield an empty key.
565 if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
566 !sanitized_username.empty()) {
567 policy_key_path_ = user_policy_key_dir_.Append(
568 base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
570 SampleValidationFailure(VALIDATION_FAILURE_DBUS);
572 ReloadPolicyKey(callback);
575 } // namespace policy