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