user_cloud_policy_store_chromeos.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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 "chrome/browser/policy/proto/cloud/device_management_local.pb.h"
20#include "chromeos/dbus/cryptohome_client.h"
21#include "chromeos/dbus/session_manager_client.h"
22#include "google_apis/gaia/gaia_auth_util.h"
23#include "policy/proto/cloud_policy.pb.h"
24
25namespace em = enterprise_management;
26
27namespace policy {
28
29namespace {
30
31// Path within |user_policy_key_dir_| that contains the policy key.
32// "%s" must be substituted with the sanitized username.
33const base::FilePath::CharType kPolicyKeyFile[] =
34    FILE_PATH_LITERAL("%s/policy.pub");
35
36// Maximum key size that will be loaded, in bytes.
37const int kKeySizeLimit = 16 * 1024;
38
39enum ValidationFailure {
40  VALIDATION_FAILURE_DBUS,
41  VALIDATION_FAILURE_LOAD_KEY,
42  VALIDATION_FAILURE_SIZE,
43};
44
45void SampleValidationFailure(ValidationFailure sample) {
46  UMA_HISTOGRAM_ENUMERATION("Enterprise.UserPolicyValidationFailure",
47                            sample,
48                            VALIDATION_FAILURE_SIZE);
49}
50
51}  // namespace
52
53// Helper class for loading legacy policy caches.
54class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate,
55                                public UserPolicyDiskCache::Delegate {
56 public:
57  typedef base::Callback<void(const std::string&,
58                              const std::string&,
59                              CloudPolicyStore::Status,
60                              scoped_ptr<em::PolicyFetchResponse>)> Callback;
61
62  LegacyPolicyCacheLoader(
63      const base::FilePath& token_cache_file,
64      const base::FilePath& policy_cache_file,
65      scoped_refptr<base::SequencedTaskRunner> background_task_runner);
66  virtual ~LegacyPolicyCacheLoader();
67
68  // Starts loading, and reports the result to |callback| when done.
69  void Load(const Callback& callback);
70
71  // UserPolicyTokenLoader::Delegate:
72  virtual void OnTokenLoaded(const std::string& token,
73                             const std::string& device_id) OVERRIDE;
74
75  // UserPolicyDiskCache::Delegate:
76  virtual void OnDiskCacheLoaded(
77      UserPolicyDiskCache::LoadResult result,
78      const em::CachedCloudPolicyResponse& policy) OVERRIDE;
79
80 private:
81  // Checks whether the load operations from the legacy caches completed. If so,
82  // fires the appropriate notification.
83  void CheckLoadFinished();
84
85  // Maps a disk cache LoadResult to a CloudPolicyStore::Status.
86  static CloudPolicyStore::Status TranslateLoadResult(
87      UserPolicyDiskCache::LoadResult result);
88
89  base::WeakPtrFactory<LegacyPolicyCacheLoader> weak_factory_;
90
91  scoped_refptr<UserPolicyTokenLoader> token_loader_;
92  scoped_refptr<UserPolicyDiskCache> policy_cache_;
93
94  std::string dm_token_;
95  std::string device_id_;
96  bool has_policy_;
97  scoped_ptr<em::PolicyFetchResponse> policy_;
98  CloudPolicyStore::Status status_;
99
100  Callback callback_;
101
102  DISALLOW_COPY_AND_ASSIGN(LegacyPolicyCacheLoader);
103};
104
105LegacyPolicyCacheLoader::LegacyPolicyCacheLoader(
106    const base::FilePath& token_cache_file,
107    const base::FilePath& policy_cache_file,
108    scoped_refptr<base::SequencedTaskRunner> background_task_runner)
109    : weak_factory_(this),
110      has_policy_(false),
111      status_(CloudPolicyStore::STATUS_OK) {
112  token_loader_ = new UserPolicyTokenLoader(weak_factory_.GetWeakPtr(),
113                                            token_cache_file,
114                                            background_task_runner);
115  policy_cache_ = new UserPolicyDiskCache(weak_factory_.GetWeakPtr(),
116                                          policy_cache_file,
117                                          background_task_runner);
118}
119
120LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {}
121
122void LegacyPolicyCacheLoader::Load(const Callback& callback) {
123  callback_ = callback;
124  token_loader_->Load();
125  policy_cache_->Load();
126}
127
128void LegacyPolicyCacheLoader::OnTokenLoaded(const std::string& token,
129                                            const std::string& device_id) {
130  dm_token_ = token;
131  device_id_ = device_id;
132  token_loader_ = NULL;
133  CheckLoadFinished();
134}
135
136void LegacyPolicyCacheLoader::OnDiskCacheLoaded(
137    UserPolicyDiskCache::LoadResult result,
138    const em::CachedCloudPolicyResponse& policy) {
139  status_ = TranslateLoadResult(result);
140  if (result == UserPolicyDiskCache::LOAD_RESULT_SUCCESS) {
141    if (policy.has_cloud_policy())
142      policy_.reset(new em::PolicyFetchResponse(policy.cloud_policy()));
143  } else {
144    LOG(WARNING) << "Failed to load legacy policy cache: " << result;
145  }
146  policy_cache_ = NULL;
147  CheckLoadFinished();
148}
149
150void LegacyPolicyCacheLoader::CheckLoadFinished() {
151  if (!token_loader_.get() && !policy_cache_.get())
152    callback_.Run(dm_token_, device_id_, status_, policy_.Pass());
153}
154
155// static
156CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult(
157    UserPolicyDiskCache::LoadResult result) {
158  switch (result) {
159    case UserPolicyDiskCache::LOAD_RESULT_SUCCESS:
160    case UserPolicyDiskCache::LOAD_RESULT_NOT_FOUND:
161      return CloudPolicyStore::STATUS_OK;
162    case UserPolicyDiskCache::LOAD_RESULT_PARSE_ERROR:
163    case UserPolicyDiskCache::LOAD_RESULT_READ_ERROR:
164      return CloudPolicyStore::STATUS_LOAD_ERROR;
165  }
166  NOTREACHED();
167  return CloudPolicyStore::STATUS_OK;
168}
169
170UserCloudPolicyStoreChromeOS::UserCloudPolicyStoreChromeOS(
171    chromeos::CryptohomeClient* cryptohome_client,
172    chromeos::SessionManagerClient* session_manager_client,
173    scoped_refptr<base::SequencedTaskRunner> background_task_runner,
174    const std::string& username,
175    const base::FilePath& user_policy_key_dir,
176    const base::FilePath& legacy_token_cache_file,
177    const base::FilePath& legacy_policy_cache_file)
178    : UserCloudPolicyStoreBase(background_task_runner),
179      cryptohome_client_(cryptohome_client),
180      session_manager_client_(session_manager_client),
181      username_(username),
182      user_policy_key_dir_(user_policy_key_dir),
183      weak_factory_(this),
184      legacy_cache_dir_(legacy_token_cache_file.DirName()),
185      legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file,
186                                                 legacy_policy_cache_file,
187                                                 background_task_runner)),
188      legacy_caches_loaded_(false),
189      policy_key_loaded_(false) {}
190
191UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {}
192
193void UserCloudPolicyStoreChromeOS::Store(
194    const em::PolicyFetchResponse& policy) {
195  // Cancel all pending requests.
196  weak_factory_.InvalidateWeakPtrs();
197  scoped_ptr<em::PolicyFetchResponse> response(
198      new em::PolicyFetchResponse(policy));
199  EnsurePolicyKeyLoaded(
200      base::Bind(&UserCloudPolicyStoreChromeOS::ValidatePolicyForStore,
201                 weak_factory_.GetWeakPtr(),
202                 base::Passed(&response)));
203}
204
205void UserCloudPolicyStoreChromeOS::Load() {
206  // Cancel all pending requests.
207  weak_factory_.InvalidateWeakPtrs();
208  session_manager_client_->RetrievePolicyForUser(
209      username_,
210      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved,
211                 weak_factory_.GetWeakPtr()));
212}
213
214void UserCloudPolicyStoreChromeOS::LoadImmediately() {
215  // This blocking DBus call is in the startup path and will block the UI
216  // thread. This only happens when the Profile is created synchronously, which
217  // on ChromeOS happens whenever the browser is restarted into the same
218  // session. That happens when the browser crashes, or right after signin if
219  // the user has flags configured in about:flags.
220  // However, on those paths we must load policy synchronously so that the
221  // Profile initialization never sees unmanaged prefs, which would lead to
222  // data loss. http://crbug.com/263061
223  std::string policy_blob =
224      session_manager_client_->BlockingRetrievePolicyForUser(username_);
225  if (policy_blob.empty()) {
226    // The session manager doesn't have policy, or the call failed.
227    // Just notify that the load is done, and don't bother with the legacy
228    // caches in this case.
229    NotifyStoreLoaded();
230    return;
231  }
232
233  scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
234  if (!policy->ParseFromString(policy_blob)) {
235    status_ = STATUS_PARSE_ERROR;
236    NotifyStoreError();
237    return;
238  }
239
240  std::string sanitized_username =
241      cryptohome_client_->BlockingGetSanitizedUsername(username_);
242  if (sanitized_username.empty()) {
243    status_ = STATUS_LOAD_ERROR;
244    NotifyStoreError();
245    return;
246  }
247
248  policy_key_path_ = user_policy_key_dir_.Append(
249      base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
250  LoadPolicyKey(policy_key_path_, &policy_key_);
251  policy_key_loaded_ = true;
252
253  scoped_ptr<UserCloudPolicyValidator> validator =
254      CreateValidator(policy.Pass(),
255                      CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
256  validator->ValidateUsername(username_);
257  const bool allow_rotation = false;
258  validator->ValidateSignature(policy_key_, allow_rotation);
259  validator->RunValidation();
260  OnRetrievedPolicyValidated(validator.get());
261}
262
263void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
264    scoped_ptr<em::PolicyFetchResponse> policy) {
265  // Create and configure a validator.
266  scoped_ptr<UserCloudPolicyValidator> validator =
267      CreateValidator(policy.Pass(),
268                      CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
269  validator->ValidateUsername(username_);
270  if (policy_key_.empty()) {
271    validator->ValidateInitialKey();
272  } else {
273    const bool allow_rotation = true;
274    validator->ValidateSignature(policy_key_, allow_rotation);
275  }
276
277  // Start validation. The Validator will delete itself once validation is
278  // complete.
279  validator.release()->StartValidation(
280      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
281                 weak_factory_.GetWeakPtr()));
282}
283
284void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
285    UserCloudPolicyValidator* validator) {
286  validation_status_ = validator->status();
287
288  UMA_HISTOGRAM_ENUMERATION(
289      "Enterprise.UserPolicyValidationStoreStatus",
290      validation_status_,
291      UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
292
293  if (!validator->success()) {
294    status_ = STATUS_VALIDATION_ERROR;
295    NotifyStoreError();
296    return;
297  }
298
299  std::string policy_blob;
300  if (!validator->policy()->SerializeToString(&policy_blob)) {
301    status_ = STATUS_SERIALIZE_ERROR;
302    NotifyStoreError();
303    return;
304  }
305
306  session_manager_client_->StorePolicyForUser(
307      username_,
308      policy_blob,
309      validator->policy()->new_public_key(),
310      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
311                 weak_factory_.GetWeakPtr()));
312}
313
314void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
315  if (!success) {
316    status_ = STATUS_STORE_ERROR;
317    NotifyStoreError();
318  } else {
319    // Load the policy right after storing it, to make sure it was accepted by
320    // the session manager. An additional validation is performed after the
321    // load; reload the key for that validation too, in case it was rotated.
322    ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
323                               weak_factory_.GetWeakPtr()));
324  }
325}
326
327void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
328    const std::string& policy_blob) {
329  if (policy_blob.empty()) {
330    // Policy fetch failed. Try legacy caches if we haven't done that already.
331    if (!legacy_caches_loaded_ && legacy_loader_.get()) {
332      legacy_caches_loaded_ = true;
333      legacy_loader_->Load(
334          base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished,
335                     weak_factory_.GetWeakPtr()));
336    } else {
337      // session_manager doesn't have policy. Adjust internal state and notify
338      // the world about the policy update.
339      policy_.reset();
340      NotifyStoreLoaded();
341    }
342    return;
343  }
344
345  // Policy is supplied by session_manager. Disregard legacy data from now on.
346  legacy_loader_.reset();
347
348  scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
349  if (!policy->ParseFromString(policy_blob)) {
350    status_ = STATUS_PARSE_ERROR;
351    NotifyStoreError();
352    return;
353  }
354
355  // Load |policy_key_| to verify the loaded policy.
356  EnsurePolicyKeyLoaded(
357      base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
358                 weak_factory_.GetWeakPtr(),
359                 base::Passed(&policy)));
360}
361
362void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
363    scoped_ptr<em::PolicyFetchResponse> policy) {
364  // Create and configure a validator for the loaded policy.
365  scoped_ptr<UserCloudPolicyValidator> validator =
366      CreateValidator(policy.Pass(),
367                      CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
368  validator->ValidateUsername(username_);
369  const bool allow_rotation = false;
370  validator->ValidateSignature(policy_key_, allow_rotation);
371  // Start validation. The Validator will delete itself once validation is
372  // complete.
373  validator.release()->StartValidation(
374      base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
375                 weak_factory_.GetWeakPtr()));
376}
377
378void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
379    UserCloudPolicyValidator* validator) {
380  validation_status_ = validator->status();
381
382  UMA_HISTOGRAM_ENUMERATION(
383      "Enterprise.UserPolicyValidationLoadStatus",
384      validation_status_,
385      UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
386
387  if (!validator->success()) {
388    status_ = STATUS_VALIDATION_ERROR;
389    NotifyStoreError();
390    return;
391  }
392
393  InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
394  status_ = STATUS_OK;
395
396  // Policy has been loaded successfully. This indicates that new-style policy
397  // is working, so the legacy cache directory can be removed.
398  if (!legacy_cache_dir_.empty()) {
399    background_task_runner()->PostTask(
400        FROM_HERE,
401        base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
402                   legacy_cache_dir_));
403    legacy_cache_dir_.clear();
404  }
405  NotifyStoreLoaded();
406}
407
408void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
409    const std::string& dm_token,
410    const std::string& device_id,
411    Status status,
412    scoped_ptr<em::PolicyFetchResponse> policy) {
413  status_ = status;
414  if (policy.get()) {
415    // Create and configure a validator for the loaded legacy policy. Note that
416    // the signature on this policy is not verified.
417    scoped_ptr<UserCloudPolicyValidator> validator =
418        CreateValidator(policy.Pass(),
419                        CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
420    validator->ValidateUsername(username_);
421    validator.release()->StartValidation(
422        base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
423                   weak_factory_.GetWeakPtr(),
424                   dm_token,
425                   device_id));
426  } else {
427    InstallLegacyTokens(dm_token, device_id);
428  }
429}
430
431void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
432    const std::string& dm_token,
433    const std::string& device_id,
434    UserCloudPolicyValidator* validator) {
435  validation_status_ = validator->status();
436  if (validator->success()) {
437    status_ = STATUS_OK;
438    InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
439
440    // Clear the public key version. The public key version field would
441    // otherwise indicate that we have key installed in the store when in fact
442    // we haven't. This may result in policy updates failing signature
443    // verification.
444    policy_->clear_public_key_version();
445  } else {
446    status_ = STATUS_VALIDATION_ERROR;
447  }
448
449  InstallLegacyTokens(dm_token, device_id);
450}
451
452void UserCloudPolicyStoreChromeOS::InstallLegacyTokens(
453    const std::string& dm_token,
454    const std::string& device_id) {
455  // Write token and device ID to |policy_|, giving them precedence over the
456  // policy blob. This is to match the legacy behavior, which used token and
457  // device id exclusively from the token cache file.
458  if (!dm_token.empty() && !device_id.empty()) {
459    if (!policy_.get())
460      policy_.reset(new em::PolicyData());
461    policy_->set_request_token(dm_token);
462    policy_->set_device_id(device_id);
463  }
464
465  // Tell the rest of the world that the policy load completed.
466  NotifyStoreLoaded();
467}
468
469// static
470void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
471    const base::FilePath& dir) {
472  if (base::PathExists(dir) && !base::DeleteFile(dir, true))
473    LOG(ERROR) << "Failed to remove cache dir " << dir.value();
474}
475
476void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
477    const base::Closure& callback) {
478  std::vector<uint8>* key = new std::vector<uint8>();
479  background_task_runner()->PostTaskAndReply(
480      FROM_HERE,
481      base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
482                 policy_key_path_,
483                 key),
484      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
485                 weak_factory_.GetWeakPtr(),
486                 base::Owned(key),
487                 callback));
488}
489
490// static
491void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
492                                                 std::vector<uint8>* key) {
493  if (!base::PathExists(path)) {
494    // There is no policy key the first time that a user fetches policy. If
495    // |path| does not exist then that is the most likely scenario, so there's
496    // no need to sample a failure.
497    VLOG(1) << "No key at " << path.value();
498    return;
499  }
500
501  int64 size;
502  if (!file_util::GetFileSize(path, &size)) {
503    LOG(ERROR) << "Could not get size of " << path.value();
504  } else if (size == 0 || size > kKeySizeLimit) {
505    LOG(ERROR) << "Key at " << path.value() << " has bad size " << size;
506  } else {
507    key->resize(size);
508    int read_size = file_util::ReadFile(
509        path, reinterpret_cast<char*>(vector_as_array(key)), size);
510    if (read_size != size) {
511      LOG(ERROR) << "Failed to read key at " << path.value();
512      key->clear();
513    }
514  }
515
516  if (key->empty())
517    SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
518}
519
520void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
521    std::vector<uint8>* key,
522    const base::Closure& callback) {
523  policy_key_.swap(*key);
524  policy_key_loaded_ = true;
525  callback.Run();
526}
527
528void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
529    const base::Closure& callback) {
530  if (policy_key_loaded_) {
531    callback.Run();
532  } else {
533    // Get the hashed username that's part of the key's path, to determine
534    // |policy_key_path_|.
535    cryptohome_client_->GetSanitizedUsername(username_,
536        base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
537                   weak_factory_.GetWeakPtr(),
538                   callback));
539  }
540}
541
542void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
543    const base::Closure& callback,
544    chromeos::DBusMethodCallStatus call_status,
545    const std::string& sanitized_username) {
546  // The default empty path will always yield an empty key.
547  if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
548      !sanitized_username.empty()) {
549    policy_key_path_ = user_policy_key_dir_.Append(
550        base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
551  } else {
552    SampleValidationFailure(VALIDATION_FAILURE_DBUS);
553  }
554  ReloadPolicyKey(callback);
555}
556
557}  // namespace policy
558