user_cloud_policy_store_chromeos.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/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/cloud/proto/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    : ALLOW_THIS_IN_INITIALIZER_LIST(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      ALLOW_THIS_IN_INITIALIZER_LIST(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_->RetrieveUserPolicy(
201      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyRetrieved,
202                 weak_factory_.GetWeakPtr()));
203}
204
205void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
206    scoped_ptr<em::PolicyFetchResponse> policy) {
207  // Create and configure a validator.
208  scoped_ptr<UserCloudPolicyValidator> validator =
209      CreateValidator(policy.Pass());
210  validator->ValidateUsername(username_);
211  if (policy_key_.empty()) {
212    validator->ValidateInitialKey();
213  } else {
214    const bool allow_rotation = true;
215    validator->ValidateSignature(policy_key_, allow_rotation);
216  }
217
218  // Start validation. The Validator will delete itself once validation is
219  // complete.
220  validator.release()->StartValidation(
221      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
222                 weak_factory_.GetWeakPtr()));
223}
224
225void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
226    UserCloudPolicyValidator* validator) {
227  validation_status_ = validator->status();
228
229  UMA_HISTOGRAM_ENUMERATION(
230      "Enterprise.UserPolicyValidationStoreStatus",
231      validation_status_,
232      UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
233
234  if (!validator->success()) {
235    status_ = STATUS_VALIDATION_ERROR;
236    NotifyStoreError();
237    return;
238  }
239
240  std::string policy_blob;
241  if (!validator->policy()->SerializeToString(&policy_blob)) {
242    status_ = STATUS_SERIALIZE_ERROR;
243    NotifyStoreError();
244    return;
245  }
246
247  session_manager_client_->StoreUserPolicy(
248      policy_blob,
249      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
250                 weak_factory_.GetWeakPtr()));
251}
252
253void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
254  if (!success) {
255    status_ = STATUS_STORE_ERROR;
256    NotifyStoreError();
257  } else {
258    // Load the policy right after storing it, to make sure it was accepted by
259    // the session manager. An additional validation is performed after the
260    // load; reload the key for that validation too, in case it was rotated.
261    ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
262                               weak_factory_.GetWeakPtr()));
263  }
264}
265
266void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
267    const std::string& policy_blob) {
268  if (policy_blob.empty()) {
269    // Policy fetch failed. Try legacy caches if we haven't done that already.
270    if (!legacy_caches_loaded_ && legacy_loader_.get()) {
271      legacy_caches_loaded_ = true;
272      legacy_loader_->Load(
273          base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished,
274                     weak_factory_.GetWeakPtr()));
275    } else {
276      // session_manager doesn't have policy. Adjust internal state and notify
277      // the world about the policy update.
278      policy_.reset();
279      NotifyStoreLoaded();
280    }
281    return;
282  }
283
284  // Policy is supplied by session_manager. Disregard legacy data from now on.
285  legacy_loader_.reset();
286
287  scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
288  if (!policy->ParseFromString(policy_blob)) {
289    status_ = STATUS_PARSE_ERROR;
290    NotifyStoreError();
291    return;
292  }
293
294  // Load |policy_key_| to verify the loaded policy.
295  EnsurePolicyKeyLoaded(
296      base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
297                 weak_factory_.GetWeakPtr(),
298                 base::Passed(&policy)));
299}
300
301void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
302    scoped_ptr<em::PolicyFetchResponse> policy) {
303  // Create and configure a validator for the loaded policy.
304  scoped_ptr<UserCloudPolicyValidator> validator =
305      CreateValidator(policy.Pass());
306  validator->ValidateUsername(username_);
307  const bool allow_rotation = false;
308  validator->ValidateSignature(policy_key_, allow_rotation);
309  // Start validation. The Validator will delete itself once validation is
310  // complete.
311  validator.release()->StartValidation(
312      base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
313                 weak_factory_.GetWeakPtr()));
314}
315
316void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
317    UserCloudPolicyValidator* validator) {
318  validation_status_ = validator->status();
319
320  UMA_HISTOGRAM_ENUMERATION(
321      "Enterprise.UserPolicyValidationLoadStatus",
322      validation_status_,
323      UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
324
325  if (!validator->success()) {
326    status_ = STATUS_VALIDATION_ERROR;
327    NotifyStoreError();
328    return;
329  }
330
331  InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
332  status_ = STATUS_OK;
333
334  // Policy has been loaded successfully. This indicates that new-style policy
335  // is working, so the legacy cache directory can be removed.
336  if (!legacy_cache_dir_.empty()) {
337    content::BrowserThread::PostBlockingPoolTask(
338        FROM_HERE,
339        base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
340                   legacy_cache_dir_));
341    legacy_cache_dir_.clear();
342  }
343  NotifyStoreLoaded();
344}
345
346void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
347    const std::string& dm_token,
348    const std::string& device_id,
349    Status status,
350    scoped_ptr<em::PolicyFetchResponse> policy) {
351  status_ = status;
352  if (policy.get()) {
353    // Create and configure a validator for the loaded legacy policy. Note that
354    // the signature on this policy is not verified.
355    scoped_ptr<UserCloudPolicyValidator> validator =
356        CreateValidator(policy.Pass());
357    validator->ValidateUsername(username_);
358    validator.release()->StartValidation(
359        base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
360                   weak_factory_.GetWeakPtr(),
361                   dm_token,
362                   device_id));
363  } else {
364    InstallLegacyTokens(dm_token, device_id);
365  }
366}
367
368void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
369    const std::string& dm_token,
370    const std::string& device_id,
371    UserCloudPolicyValidator* validator) {
372  validation_status_ = validator->status();
373  if (validator->success()) {
374    status_ = STATUS_OK;
375    InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
376
377    // Clear the public key version. The public key version field would
378    // otherwise indicate that we have key installed in the store when in fact
379    // we haven't. This may result in policy updates failing signature
380    // verification.
381    policy_->clear_public_key_version();
382  } else {
383    status_ = STATUS_VALIDATION_ERROR;
384  }
385
386  InstallLegacyTokens(dm_token, device_id);
387}
388
389void UserCloudPolicyStoreChromeOS::InstallLegacyTokens(
390    const std::string& dm_token,
391    const std::string& device_id) {
392  // Write token and device ID to |policy_|, giving them precedence over the
393  // policy blob. This is to match the legacy behavior, which used token and
394  // device id exclusively from the token cache file.
395  if (!dm_token.empty() && !device_id.empty()) {
396    if (!policy_.get())
397      policy_.reset(new em::PolicyData());
398    policy_->set_request_token(dm_token);
399    policy_->set_device_id(device_id);
400  }
401
402  // Tell the rest of the world that the policy load completed.
403  NotifyStoreLoaded();
404}
405
406// static
407void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
408    const base::FilePath& dir) {
409  if (file_util::PathExists(dir) && !file_util::Delete(dir, true))
410    LOG(ERROR) << "Failed to remove cache dir " << dir.value();
411}
412
413void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
414    const base::Closure& callback) {
415  std::vector<uint8>* key = new std::vector<uint8>();
416  content::BrowserThread::PostBlockingPoolTaskAndReply(
417      FROM_HERE,
418      base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
419                 policy_key_path_,
420                 key),
421      base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
422                 weak_factory_.GetWeakPtr(),
423                 base::Owned(key),
424                 callback));
425}
426
427// static
428void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
429                                                 std::vector<uint8>* key) {
430  if (!file_util::PathExists(path)) {
431    // There is no policy key the first time that a user fetches policy. If
432    // |path| does not exist then that is the most likely scenario, so there's
433    // no need to sample a failure.
434    VLOG(1) << "No key at " << path.value();
435    return;
436  }
437
438  int64 size;
439  if (!file_util::GetFileSize(path, &size)) {
440    LOG(ERROR) << "Could not get size of " << path.value();
441  } else if (size == 0 || size > kKeySizeLimit) {
442    LOG(ERROR) << "Key at " << path.value() << " has bad size " << size;
443  } else {
444    key->resize(size);
445    int read_size = file_util::ReadFile(
446        path, reinterpret_cast<char*>(vector_as_array(key)), size);
447    if (read_size != size) {
448      LOG(ERROR) << "Failed to read key at " << path.value();
449      key->clear();
450    }
451  }
452
453  if (key->empty())
454    SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
455}
456
457void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
458    std::vector<uint8>* key,
459    const base::Closure& callback) {
460  policy_key_.swap(*key);
461  policy_key_loaded_ = true;
462  callback.Run();
463}
464
465void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
466    const base::Closure& callback) {
467  if (policy_key_loaded_) {
468    callback.Run();
469  } else {
470    // Get the hashed username that's part of the key's path, to determine
471    // |policy_key_path_|.
472    cryptohome_client_->GetSanitizedUsername(username_,
473        base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
474                   weak_factory_.GetWeakPtr(),
475                   callback));
476  }
477}
478
479void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
480    const base::Closure& callback,
481    chromeos::DBusMethodCallStatus call_status,
482    const std::string& sanitized_username) {
483  // The default empty path will always yield an empty key.
484  if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
485      !sanitized_username.empty()) {
486    policy_key_path_ = user_policy_key_dir_.Append(
487        base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
488  } else {
489    SampleValidationFailure(VALIDATION_FAILURE_DBUS);
490  }
491  ReloadPolicyKey(callback);
492}
493
494}  // namespace policy
495