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