user_cloud_policy_store_chromeos.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
26namespace em = enterprise_management;
27
28namespace policy {
29
30namespace {
31
32// Path within |user_policy_key_dir_| that contains the policy key.
33// "%s" must be substituted with the sanitized username.
34const base::FilePath::CharType kPolicyKeyFile[] =
35    FILE_PATH_LITERAL("%s/policy.pub");
36
37// Maximum key size that will be loaded, in bytes.
38const size_t kKeySizeLimit = 16 * 1024;
39
40enum ValidationFailure {
41  VALIDATION_FAILURE_DBUS,
42  VALIDATION_FAILURE_LOAD_KEY,
43  VALIDATION_FAILURE_SIZE,
44};
45
46void 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.
53std::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.
60class 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
111LegacyPolicyCacheLoader::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
126LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {}
127
128void LegacyPolicyCacheLoader::Load(const Callback& callback) {
129  callback_ = callback;
130  token_loader_->Load();
131  policy_cache_->Load();
132}
133
134void 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
142void 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
156void LegacyPolicyCacheLoader::CheckLoadFinished() {
157  if (!token_loader_.get() && !policy_cache_.get())
158    callback_.Run(dm_token_, device_id_, status_, policy_.Pass());
159}
160
161// static
162CloudPolicyStore::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
176UserCloudPolicyStoreChromeOS::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
197UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {}
198
199void 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
211void 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
220void 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
265void 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
290void 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
319void 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
332void 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
367void 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
379void 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
409void 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
432void 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
453void 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
471void 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
477void 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
492void 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
518void 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
526void 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
540void 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
555scoped_ptr<UserCloudPolicyValidator>
556UserCloudPolicyStoreChromeOS::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