Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / policy / user_cloud_policy_store_chromeos.cc
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
26 namespace em = enterprise_management;
27
28 namespace policy {
29
30 namespace {
31
32 // Path within |user_policy_key_dir_| that contains the policy key.
33 // "%s" must be substituted with the sanitized username.
34 const base::FilePath::CharType kPolicyKeyFile[] =
35     FILE_PATH_LITERAL("%s/policy.pub");
36
37 // Maximum key size that will be loaded, in bytes.
38 const int kKeySizeLimit = 16 * 1024;
39
40 enum ValidationFailure {
41   VALIDATION_FAILURE_DBUS,
42   VALIDATION_FAILURE_LOAD_KEY,
43   VALIDATION_FAILURE_SIZE,
44 };
45
46 void 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.
53 std::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.
60 class 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
111 LegacyPolicyCacheLoader::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
126 LegacyPolicyCacheLoader::~LegacyPolicyCacheLoader() {}
127
128 void LegacyPolicyCacheLoader::Load(const Callback& callback) {
129   callback_ = callback;
130   token_loader_->Load();
131   policy_cache_->Load();
132 }
133
134 void 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
142 void 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
156 void LegacyPolicyCacheLoader::CheckLoadFinished() {
157   if (!token_loader_.get() && !policy_cache_.get())
158     callback_.Run(dm_token_, device_id_, status_, policy_.Pass());
159 }
160
161 // static
162 CloudPolicyStore::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
176 UserCloudPolicyStoreChromeOS::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
197 UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {}
198
199 void 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
211 void 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
220 void 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 =
260       CreateValidator(policy.Pass(),
261                       CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
262   validator->ValidateUsername(username_);
263   const bool allow_rotation = false;
264   validator->ValidateSignature(
265       policy_key_,
266       GetPolicyVerificationKey(),
267       ExtractDomain(sanitized_username),
268       allow_rotation);
269   validator->RunValidation();
270   OnRetrievedPolicyValidated(validator.get());
271 }
272
273 void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
274     scoped_ptr<em::PolicyFetchResponse> policy) {
275   // Create and configure a validator.
276   scoped_ptr<UserCloudPolicyValidator> validator =
277       CreateValidator(policy.Pass(),
278                       CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
279   validator->ValidateUsername(username_);
280   if (policy_key_.empty()) {
281     validator->ValidateInitialKey(GetPolicyVerificationKey(),
282                                   ExtractDomain(username_));
283   } else {
284     const bool allow_rotation = true;
285     validator->ValidateSignature(policy_key_,
286                                  GetPolicyVerificationKey(),
287                                  ExtractDomain(username_),
288                                  allow_rotation);
289   }
290
291   // Start validation. The Validator will delete itself once validation is
292   // complete.
293   validator.release()->StartValidation(
294       base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
295                  weak_factory_.GetWeakPtr()));
296 }
297
298 void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
299     UserCloudPolicyValidator* validator) {
300   validation_status_ = validator->status();
301
302   UMA_HISTOGRAM_ENUMERATION(
303       "Enterprise.UserPolicyValidationStoreStatus",
304       validation_status_,
305       UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
306
307   if (!validator->success()) {
308     status_ = STATUS_VALIDATION_ERROR;
309     NotifyStoreError();
310     return;
311   }
312
313   std::string policy_blob;
314   if (!validator->policy()->SerializeToString(&policy_blob)) {
315     status_ = STATUS_SERIALIZE_ERROR;
316     NotifyStoreError();
317     return;
318   }
319
320   session_manager_client_->StorePolicyForUser(
321       username_,
322       policy_blob,
323       validator->policy()->new_public_key(),
324       base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
325                  weak_factory_.GetWeakPtr()));
326 }
327
328 void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
329   if (!success) {
330     status_ = STATUS_STORE_ERROR;
331     NotifyStoreError();
332   } else {
333     // Load the policy right after storing it, to make sure it was accepted by
334     // the session manager. An additional validation is performed after the
335     // load; reload the key for that validation too, in case it was rotated.
336     ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
337                                weak_factory_.GetWeakPtr()));
338   }
339 }
340
341 void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
342     const std::string& policy_blob) {
343   if (policy_blob.empty()) {
344     // Policy fetch failed. Try legacy caches if we haven't done that already.
345     if (!legacy_caches_loaded_ && legacy_loader_.get()) {
346       legacy_caches_loaded_ = true;
347       legacy_loader_->Load(
348           base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished,
349                      weak_factory_.GetWeakPtr()));
350     } else {
351       // session_manager doesn't have policy. Adjust internal state and notify
352       // the world about the policy update.
353       policy_.reset();
354       NotifyStoreLoaded();
355     }
356     return;
357   }
358
359   // Policy is supplied by session_manager. Disregard legacy data from now on.
360   legacy_loader_.reset();
361
362   scoped_ptr<em::PolicyFetchResponse> policy(new em::PolicyFetchResponse());
363   if (!policy->ParseFromString(policy_blob)) {
364     status_ = STATUS_PARSE_ERROR;
365     NotifyStoreError();
366     return;
367   }
368
369   // Load |policy_key_| to verify the loaded policy.
370   EnsurePolicyKeyLoaded(
371       base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
372                  weak_factory_.GetWeakPtr(),
373                  base::Passed(&policy)));
374 }
375
376 void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
377     scoped_ptr<em::PolicyFetchResponse> policy) {
378   // Create and configure a validator for the loaded policy.
379   scoped_ptr<UserCloudPolicyValidator> validator =
380       CreateValidator(policy.Pass(),
381                       CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
382   validator->ValidateUsername(username_);
383   const bool allow_rotation = false;
384   validator->ValidateSignature(policy_key_,
385                                GetPolicyVerificationKey(),
386                                ExtractDomain(username_),
387                                allow_rotation);
388   // Start validation. The Validator will delete itself once validation is
389   // complete.
390   validator.release()->StartValidation(
391       base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
392                  weak_factory_.GetWeakPtr()));
393 }
394
395 void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
396     UserCloudPolicyValidator* validator) {
397   validation_status_ = validator->status();
398
399   UMA_HISTOGRAM_ENUMERATION(
400       "Enterprise.UserPolicyValidationLoadStatus",
401       validation_status_,
402       UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
403
404   if (!validator->success()) {
405     status_ = STATUS_VALIDATION_ERROR;
406     NotifyStoreError();
407     return;
408   }
409
410   InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
411   status_ = STATUS_OK;
412
413   // Policy has been loaded successfully. This indicates that new-style policy
414   // is working, so the legacy cache directory can be removed.
415   if (!legacy_cache_dir_.empty()) {
416     background_task_runner()->PostTask(
417         FROM_HERE,
418         base::Bind(&UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir,
419                    legacy_cache_dir_));
420     legacy_cache_dir_.clear();
421   }
422   NotifyStoreLoaded();
423 }
424
425 void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
426     const std::string& dm_token,
427     const std::string& device_id,
428     Status status,
429     scoped_ptr<em::PolicyFetchResponse> policy) {
430   status_ = status;
431   if (policy.get()) {
432     // Create and configure a validator for the loaded legacy policy. Note that
433     // the signature on this policy is not verified.
434     scoped_ptr<UserCloudPolicyValidator> validator =
435         CreateValidator(policy.Pass(),
436                         CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
437     validator->ValidateUsername(username_);
438     validator.release()->StartValidation(
439         base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
440                    weak_factory_.GetWeakPtr(),
441                    dm_token,
442                    device_id));
443   } else {
444     InstallLegacyTokens(dm_token, device_id);
445   }
446 }
447
448 void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
449     const std::string& dm_token,
450     const std::string& device_id,
451     UserCloudPolicyValidator* validator) {
452   validation_status_ = validator->status();
453   if (validator->success()) {
454     status_ = STATUS_OK;
455     InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
456
457     // Clear the public key version. The public key version field would
458     // otherwise indicate that we have key installed in the store when in fact
459     // we haven't. This may result in policy updates failing signature
460     // verification.
461     policy_->clear_public_key_version();
462   } else {
463     status_ = STATUS_VALIDATION_ERROR;
464   }
465
466   InstallLegacyTokens(dm_token, device_id);
467 }
468
469 void UserCloudPolicyStoreChromeOS::InstallLegacyTokens(
470     const std::string& dm_token,
471     const std::string& device_id) {
472   // Write token and device ID to |policy_|, giving them precedence over the
473   // policy blob. This is to match the legacy behavior, which used token and
474   // device id exclusively from the token cache file.
475   if (!dm_token.empty() && !device_id.empty()) {
476     if (!policy_.get())
477       policy_.reset(new em::PolicyData());
478     policy_->set_request_token(dm_token);
479     policy_->set_device_id(device_id);
480   }
481
482   // Tell the rest of the world that the policy load completed.
483   NotifyStoreLoaded();
484 }
485
486 // static
487 void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
488     const base::FilePath& dir) {
489   if (base::PathExists(dir) && !base::DeleteFile(dir, true))
490     LOG(ERROR) << "Failed to remove cache dir " << dir.value();
491 }
492
493 void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
494     const base::Closure& callback) {
495   std::string* key = new std::string();
496   background_task_runner()->PostTaskAndReply(
497       FROM_HERE,
498       base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
499                  policy_key_path_,
500                  key),
501       base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
502                  weak_factory_.GetWeakPtr(),
503                  base::Owned(key),
504                  callback));
505 }
506
507 // static
508 void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
509                                                  std::string* key) {
510   if (!base::PathExists(path)) {
511     // There is no policy key the first time that a user fetches policy. If
512     // |path| does not exist then that is the most likely scenario, so there's
513     // no need to sample a failure.
514     VLOG(1) << "No key at " << path.value();
515     return;
516   }
517
518   int64 size;
519   key->clear();
520   if (!base::GetFileSize(path, &size)) {
521     LOG(ERROR) << "Could not get size of " << path.value();
522   } else if (size == 0 || size > kKeySizeLimit) {
523     LOG(ERROR) << "Key at " << path.value() << " has bad size " << size;
524   } else {
525     char buf[size];
526     int read_size = base::ReadFile(path, buf, size);
527     if (read_size != size) {
528       LOG(ERROR) << "Failed to read key at " << path.value();
529     } else {
530       key->append(buf, size);
531     }
532   }
533
534   if (key->empty())
535     SampleValidationFailure(VALIDATION_FAILURE_LOAD_KEY);
536 }
537
538 void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
539     std::string* key,
540     const base::Closure& callback) {
541   policy_key_ = *key;
542   policy_key_loaded_ = true;
543   callback.Run();
544 }
545
546 void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
547     const base::Closure& callback) {
548   if (policy_key_loaded_) {
549     callback.Run();
550   } else {
551     // Get the hashed username that's part of the key's path, to determine
552     // |policy_key_path_|.
553     cryptohome_client_->GetSanitizedUsername(username_,
554         base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
555                    weak_factory_.GetWeakPtr(),
556                    callback));
557   }
558 }
559
560 void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
561     const base::Closure& callback,
562     chromeos::DBusMethodCallStatus call_status,
563     const std::string& sanitized_username) {
564   // The default empty path will always yield an empty key.
565   if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
566       !sanitized_username.empty()) {
567     policy_key_path_ = user_policy_key_dir_.Append(
568         base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
569   } else {
570     SampleValidationFailure(VALIDATION_FAILURE_DBUS);
571   }
572   ReloadPolicyKey(callback);
573 }
574
575 }  // namespace policy