user_cloud_policy_store_chromeos.cc revision 010d83a9304c5a91596085d917d248abff47903a
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/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  base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_;
96
97  scoped_refptr<UserPolicyTokenLoader> token_loader_;
98  scoped_refptr<UserPolicyDiskCache> policy_cache_;
99
100  std::string dm_token_;
101  std::string device_id_;
102  bool has_policy_;
103  scoped_ptr<em::PolicyFetchResponse> policy_;
104  CloudPolicyStore::Status status_;
105
106  Callback callback_;
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    : weak_factory_(this),
116      has_policy_(false),
117      status_(CloudPolicyStore::STATUS_OK) {
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      weak_factory_(this),
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) {}
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 = CreateValidator(
260      policy.Pass(), CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
261  validator->ValidateUsername(username_, true);
262  const bool allow_rotation = false;
263  validator->ValidateSignature(
264      policy_key_,
265      GetPolicyVerificationKey(),
266      ExtractDomain(sanitized_username),
267      allow_rotation);
268  validator->RunValidation();
269  OnRetrievedPolicyValidated(validator.get());
270}
271
272void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
273    scoped_ptr<em::PolicyFetchResponse> policy) {
274  // Create and configure a validator.
275  scoped_ptr<UserCloudPolicyValidator> validator =
276      CreateValidator(policy.Pass(),
277                      CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
278  validator->ValidateUsername(username_, true);
279  if (policy_key_.empty()) {
280    validator->ValidateInitialKey(GetPolicyVerificationKey(),
281                                  ExtractDomain(username_));
282  } else {
283    const bool allow_rotation = true;
284    validator->ValidateSignature(policy_key_,
285                                 GetPolicyVerificationKey(),
286                                 ExtractDomain(username_),
287                                 allow_rotation);
288  }
289
290  // Start validation. The Validator will delete itself once validation is
291  // complete.
292  validator.release()->StartValidation(
293      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
294                 weak_factory_.GetWeakPtr()));
295}
296
297void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
298    UserCloudPolicyValidator* validator) {
299  validation_status_ = validator->status();
300
301  UMA_HISTOGRAM_ENUMERATION(
302      "Enterprise.UserPolicyValidationStoreStatus",
303      validation_status_,
304      UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
305
306  if (!validator->success()) {
307    status_ = STATUS_VALIDATION_ERROR;
308    NotifyStoreError();
309    return;
310  }
311
312  std::string policy_blob;
313  if (!validator->policy()->SerializeToString(&policy_blob)) {
314    status_ = STATUS_SERIALIZE_ERROR;
315    NotifyStoreError();
316    return;
317  }
318
319  session_manager_client_->StorePolicyForUser(
320      username_,
321      policy_blob,
322      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
323                 weak_factory_.GetWeakPtr()));
324}
325
326void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
327  if (!success) {
328    status_ = STATUS_STORE_ERROR;
329    NotifyStoreError();
330  } else {
331    // Load the policy right after storing it, to make sure it was accepted by
332    // the session manager. An additional validation is performed after the
333    // load; reload the key for that validation too, in case it was rotated.
334    ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
335                               weak_factory_.GetWeakPtr()));
336  }
337}
338
339void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
340    const std::string& policy_blob) {
341  if (policy_blob.empty()) {
342    // Policy fetch failed. Try legacy caches if we haven't done that already.
343    if (!legacy_caches_loaded_ && legacy_loader_.get()) {
344      legacy_caches_loaded_ = true;
345      legacy_loader_->Load(
346          base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished,
347                     weak_factory_.GetWeakPtr()));
348    } else {
349      // session_manager doesn't have policy. Adjust internal state and notify
350      // the world about the policy update.
351      policy_.reset();
352      NotifyStoreLoaded();
353    }
354    return;
355  }
356
357  // Policy is supplied by session_manager. Disregard legacy data from now on.
358  legacy_loader_.reset();
359
360  scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
361  if (!policy->ParseFromString(policy_blob)) {
362    status_ = STATUS_PARSE_ERROR;
363    NotifyStoreError();
364    return;
365  }
366
367  // Load |policy_key_| to verify the loaded policy.
368  EnsurePolicyKeyLoaded(
369      base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
370                 weak_factory_.GetWeakPtr(),
371                 base::Passed(&policy)));
372}
373
374void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
375    scoped_ptr<em::PolicyFetchResponse> policy) {
376  // Create and configure a validator for the loaded policy.
377  scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
378      policy.Pass(), CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
379  validator->ValidateUsername(username_, true);
380  const bool allow_rotation = false;
381  validator->ValidateSignature(policy_key_,
382                               GetPolicyVerificationKey(),
383                               ExtractDomain(username_),
384                               allow_rotation);
385  // Start validation. The Validator will delete itself once validation is
386  // complete.
387  validator.release()->StartValidation(
388      base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
389                 weak_factory_.GetWeakPtr()));
390}
391
392void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
393    UserCloudPolicyValidator* validator) {
394  validation_status_ = validator->status();
395
396  UMA_HISTOGRAM_ENUMERATION(
397      "Enterprise.UserPolicyValidationLoadStatus",
398      validation_status_,
399      UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
400
401  if (!validator->success()) {
402    status_ = STATUS_VALIDATION_ERROR;
403    NotifyStoreError();
404    return;
405  }
406
407  InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
408  status_ = STATUS_OK;
409
410  // Policy has been loaded successfully. This indicates that new-style policy
411  // is working, so the legacy cache directory can be removed.
412  if (!legacy_cache_dir_.empty()) {
413    background_task_runner()->PostTask(
414        FROM_HERE,
415        base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
416                   legacy_cache_dir_));
417    legacy_cache_dir_.clear();
418  }
419  NotifyStoreLoaded();
420}
421
422void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
423    const std::string& dm_token,
424    const std::string& device_id,
425    Status status,
426    scoped_ptr<em::PolicyFetchResponse> policy) {
427  status_ = status;
428  if (policy.get()) {
429    // Create and configure a validator for the loaded legacy policy. Note that
430    // the signature on this policy is not verified.
431    scoped_ptr<UserCloudPolicyValidator> validator =
432        CreateValidator(policy.Pass(),
433                        CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
434    validator->ValidateUsername(username_, true);
435    validator.release()->StartValidation(
436        base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
437                   weak_factory_.GetWeakPtr(),
438                   dm_token,
439                   device_id));
440  } else {
441    InstallLegacyTokens(dm_token, device_id);
442  }
443}
444
445void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
446    const std::string& dm_token,
447    const std::string& device_id,
448    UserCloudPolicyValidator* validator) {
449  validation_status_ = validator->status();
450  if (validator->success()) {
451    status_ = STATUS_OK;
452    InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
453
454    // Clear the public key version. The public key version field would
455    // otherwise indicate that we have key installed in the store when in fact
456    // we haven't. This may result in policy updates failing signature
457    // verification.
458    policy_->clear_public_key_version();
459  } else {
460    status_ = STATUS_VALIDATION_ERROR;
461  }
462
463  InstallLegacyTokens(dm_token, device_id);
464}
465
466void UserCloudPolicyStoreChromeOS::InstallLegacyTokens(
467    const std::string& dm_token,
468    const std::string& device_id) {
469  // Write token and device ID to |policy_|, giving them precedence over the
470  // policy blob. This is to match the legacy behavior, which used token and
471  // device id exclusively from the token cache file.
472  if (!dm_token.empty() && !device_id.empty()) {
473    if (!policy_.get())
474      policy_.reset(new em::PolicyData());
475    policy_->set_request_token(dm_token);
476    policy_->set_device_id(device_id);
477  }
478
479  // Tell the rest of the world that the policy load completed.
480  NotifyStoreLoaded();
481}
482
483// static
484void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
485    const base::FilePath& dir) {
486  if (base::PathExists(dir) && !base::DeleteFile(dir, true))
487    LOG(ERROR) << "Failed to remove cache dir " << dir.value();
488}
489
490void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
491    const base::Closure& callback) {
492  std::string* key = new std::string();
493  background_task_runner()->PostTaskAndReply(
494      FROM_HERE,
495      base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
496                 policy_key_path_,
497                 key),
498      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
499                 weak_factory_.GetWeakPtr(),
500                 base::Owned(key),
501                 callback));
502}
503
504// static
505void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
506                                                 std::string* key) {
507  if (!base::PathExists(path)) {
508    // There is no policy key the first time that a user fetches policy. If
509    // |path| does not exist then that is the most likely scenario, so there's
510    // no need to sample a failure.
511    VLOG(1) << "No key at " << path.value();
512    return;
513  }
514
515  const bool read_success = base::ReadFileToString(path, key, kKeySizeLimit);
516  // If the read was successful and the file size is 0 or if the read fails
517  // due to file size exceeding |kKeySizeLimit|, log error.
518  if ((read_success && key->length() == 0) ||
519      (!read_success && key->length() == kKeySizeLimit)) {
520    LOG(ERROR) << "Key at " << path.value()
521               << (read_success ? " is empty." : " exceeds size limit");
522    key->clear();
523  } else if (!read_success) {
524    LOG(ERROR) << "Failed to read key at " << path.value();
525  }
526
527  if (key->empty())
528    SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
529}
530
531void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
532    std::string* key,
533    const base::Closure& callback) {
534  policy_key_ = *key;
535  policy_key_loaded_ = true;
536  callback.Run();
537}
538
539void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
540    const base::Closure& callback) {
541  if (policy_key_loaded_) {
542    callback.Run();
543  } else {
544    // Get the hashed username that's part of the key's path, to determine
545    // |policy_key_path_|.
546    cryptohome_client_->GetSanitizedUsername(username_,
547        base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
548                   weak_factory_.GetWeakPtr(),
549                   callback));
550  }
551}
552
553void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
554    const base::Closure& callback,
555    chromeos::DBusMethodCallStatus call_status,
556    const std::string& sanitized_username) {
557  // The default empty path will always yield an empty key.
558  if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
559      !sanitized_username.empty()) {
560    policy_key_path_ = user_policy_key_dir_.Append(
561        base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
562  } else {
563    SampleValidationFailure(VALIDATION_FAILURE_DBUS);
564  }
565  ReloadPolicyKey(callback);
566}
567
568}  // namespace policy
569