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