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