| Index: chrome/browser/policy/user_cloud_policy_store_chromeos.cc
|
| diff --git a/chrome/browser/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/policy/user_cloud_policy_store_chromeos.cc
|
| index 07c247a351a6fb42c2bf016fb57053cfd6edb52a..77eb0fe9e489af429f81d30f8391b601c5a7f98b 100644
|
| --- a/chrome/browser/policy/user_cloud_policy_store_chromeos.cc
|
| +++ b/chrome/browser/policy/user_cloud_policy_store_chromeos.cc
|
| @@ -4,17 +4,19 @@
|
|
|
| #include "chrome/browser/policy/user_cloud_policy_store_chromeos.h"
|
|
|
| -#include <string>
|
| -
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| #include "base/callback.h"
|
| #include "base/file_util.h"
|
| +#include "base/logging.h"
|
| #include "base/memory/ref_counted.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/stringprintf.h"
|
| #include "chrome/browser/policy/proto/cloud_policy.pb.h"
|
| #include "chrome/browser/policy/proto/device_management_local.pb.h"
|
| #include "chrome/browser/policy/user_policy_disk_cache.h"
|
| #include "chrome/browser/policy/user_policy_token_loader.h"
|
| +#include "chromeos/dbus/cryptohome_client.h"
|
| #include "chromeos/dbus/session_manager_client.h"
|
| #include "content/public/browser/browser_thread.h"
|
| #include "google_apis/gaia/gaia_auth_util.h"
|
| @@ -24,15 +26,16 @@ namespace em = enterprise_management;
|
| namespace policy {
|
|
|
| namespace {
|
| -// Subdirectory in the user's profile for storing user policies.
|
| -const base::FilePath::CharType kPolicyDir[] =
|
| - FILE_PATH_LITERAL("Device Management");
|
| -// File in the above directory for stroing user policy dmtokens.
|
| -const base::FilePath::CharType kTokenCacheFile[] = FILE_PATH_LITERAL("Token");
|
| -// File in the above directory for storing user policy data.
|
| -const base::FilePath::CharType kPolicyCacheFile[] = FILE_PATH_LITERAL("Policy");
|
| -} // namespace
|
|
|
| +// Path within |user_policy_key_dir_| that contains the policy key.
|
| +// "%s" must be substituted with the sanitized username.
|
| +const base::FilePath::CharType kPolicyKeyFile[] =
|
| + FILE_PATH_LITERAL("%s/policy.pub");
|
| +
|
| +// Maximum key size that will be loaded, in bytes.
|
| +const int kKeySizeLimit = 16 * 1024;
|
| +
|
| +} // namespace
|
|
|
| // Helper class for loading legacy policy caches.
|
| class LegacyPolicyCacheLoader : public UserPolicyTokenLoader::Delegate,
|
| @@ -147,17 +150,22 @@ CloudPolicyStore::Status LegacyPolicyCacheLoader::TranslateLoadResult(
|
| }
|
|
|
| UserCloudPolicyStoreChromeOS::UserCloudPolicyStoreChromeOS(
|
| + chromeos::CryptohomeClient* cryptohome_client,
|
| chromeos::SessionManagerClient* session_manager_client,
|
| const std::string& username,
|
| + const base::FilePath& user_policy_key_dir,
|
| const base::FilePath& legacy_token_cache_file,
|
| const base::FilePath& legacy_policy_cache_file)
|
| - : session_manager_client_(session_manager_client),
|
| + : cryptohome_client_(cryptohome_client),
|
| + session_manager_client_(session_manager_client),
|
| username_(username),
|
| + user_policy_key_dir_(user_policy_key_dir),
|
| ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
|
| legacy_cache_dir_(legacy_token_cache_file.DirName()),
|
| legacy_loader_(new LegacyPolicyCacheLoader(legacy_token_cache_file,
|
| legacy_policy_cache_file)),
|
| - legacy_caches_loaded_(false) {}
|
| + legacy_caches_loaded_(false),
|
| + policy_key_loaded_(false) {}
|
|
|
| UserCloudPolicyStoreChromeOS::~UserCloudPolicyStoreChromeOS() {}
|
|
|
| @@ -165,10 +173,12 @@ void UserCloudPolicyStoreChromeOS::Store(
|
| const em::PolicyFetchResponse& policy) {
|
| // Cancel all pending requests.
|
| weak_factory_.InvalidateWeakPtrs();
|
| - Validate(
|
| - scoped_ptr<em::PolicyFetchResponse>(new em::PolicyFetchResponse(policy)),
|
| - base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
|
| - weak_factory_.GetWeakPtr()));
|
| + scoped_ptr<em::PolicyFetchResponse> response(
|
| + new em::PolicyFetchResponse(policy));
|
| + EnsurePolicyKeyLoaded(
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::ValidatePolicyForStore,
|
| + weak_factory_.GetWeakPtr(),
|
| + base::Passed(&response)));
|
| }
|
|
|
| void UserCloudPolicyStoreChromeOS::Load() {
|
| @@ -179,6 +189,61 @@ void UserCloudPolicyStoreChromeOS::Load() {
|
| weak_factory_.GetWeakPtr()));
|
| }
|
|
|
| +void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
|
| + scoped_ptr<em::PolicyFetchResponse> policy) {
|
| + // Create and configure a validator.
|
| + scoped_ptr<UserCloudPolicyValidator> validator =
|
| + CreateValidator(policy.Pass());
|
| + validator->ValidateUsername(username_);
|
| + if (policy_key_.empty()) {
|
| + validator->ValidateInitialKey();
|
| + } else {
|
| + const bool allow_rotation = true;
|
| + validator->ValidateSignature(policy_key_, allow_rotation);
|
| + }
|
| +
|
| + // Start validation. The Validator will delete itself once validation is
|
| + // complete.
|
| + validator.release()->StartValidation(
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
|
| + UserCloudPolicyValidator* validator) {
|
| + validation_status_ = validator->status();
|
| + if (!validator->success()) {
|
| + status_ = STATUS_VALIDATION_ERROR;
|
| + NotifyStoreError();
|
| + return;
|
| + }
|
| +
|
| + std::string policy_blob;
|
| + if (!validator->policy()->SerializeToString(&policy_blob)) {
|
| + status_ = STATUS_SERIALIZE_ERROR;
|
| + NotifyStoreError();
|
| + return;
|
| + }
|
| +
|
| + session_manager_client_->StoreUserPolicy(
|
| + policy_blob,
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
|
| + if (!success) {
|
| + status_ = STATUS_STORE_ERROR;
|
| + NotifyStoreError();
|
| + } else {
|
| + // Load the policy right after storing it, to make sure it was accepted by
|
| + // the session manager. An additional validation is performed after the
|
| + // load; reload the key for that validation too, in case it was rotated.
|
| + ReloadPolicyKey(base::Bind(&UserCloudPolicyStoreChromeOS::Load,
|
| + weak_factory_.GetWeakPtr()));
|
| + }
|
| +}
|
| +
|
| void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
|
| const std::string& policy_blob) {
|
| if (policy_blob.empty()) {
|
| @@ -207,9 +272,26 @@ void UserCloudPolicyStoreChromeOS::OnPolicyRetrieved(
|
| return;
|
| }
|
|
|
| - Validate(policy.Pass(),
|
| - base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
|
| - weak_factory_.GetWeakPtr()));
|
| + // Load |policy_key_| to verify the loaded policy.
|
| + EnsurePolicyKeyLoaded(
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy,
|
| + weak_factory_.GetWeakPtr(),
|
| + base::Passed(&policy)));
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
|
| + scoped_ptr<em::PolicyFetchResponse> policy) {
|
| + // Create and configure a validator for the loaded policy.
|
| + scoped_ptr<UserCloudPolicyValidator> validator =
|
| + CreateValidator(policy.Pass());
|
| + validator->ValidateUsername(username_);
|
| + const bool allow_rotation = false;
|
| + validator->ValidateSignature(policy_key_, allow_rotation);
|
| + // Start validation. The Validator will delete itself once validation is
|
| + // complete.
|
| + validator.release()->StartValidation(
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated,
|
| + weak_factory_.GetWeakPtr()));
|
| }
|
|
|
| void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
|
| @@ -236,56 +318,6 @@ void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
|
| NotifyStoreLoaded();
|
| }
|
|
|
| -void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
|
| - UserCloudPolicyValidator* validator) {
|
| - validation_status_ = validator->status();
|
| - if (!validator->success()) {
|
| - status_ = STATUS_VALIDATION_ERROR;
|
| - NotifyStoreError();
|
| - return;
|
| - }
|
| -
|
| - std::string policy_blob;
|
| - if (!validator->policy()->SerializeToString(&policy_blob)) {
|
| - status_ = STATUS_SERIALIZE_ERROR;
|
| - NotifyStoreError();
|
| - return;
|
| - }
|
| -
|
| - session_manager_client_->StoreUserPolicy(
|
| - policy_blob,
|
| - base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyStored,
|
| - weak_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void UserCloudPolicyStoreChromeOS::OnPolicyStored(bool success) {
|
| - if (!success) {
|
| - status_ = STATUS_STORE_ERROR;
|
| - NotifyStoreError();
|
| - } else {
|
| - // TODO(mnissler): Once we do signature verifications, we'll have to reload
|
| - // the key at this point to account for key rotations.
|
| - Load();
|
| - }
|
| -}
|
| -
|
| -void UserCloudPolicyStoreChromeOS::Validate(
|
| - scoped_ptr<em::PolicyFetchResponse> policy,
|
| - const UserCloudPolicyValidator::CompletionCallback& callback) {
|
| - // Configure the validator.
|
| - scoped_ptr<UserCloudPolicyValidator> validator =
|
| - CreateValidator(policy.Pass());
|
| - validator->ValidateUsername(username_);
|
| -
|
| - // TODO(mnissler): Do a signature check here as well. The key is stored by
|
| - // session_manager in the root-owned cryptohome area, which is currently
|
| - // inaccessible to Chrome though.
|
| -
|
| - // Start validation. The Validator will free itself once validation is
|
| - // complete.
|
| - validator.release()->StartValidation(callback);
|
| -}
|
| -
|
| void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
|
| const std::string& dm_token,
|
| const std::string& device_id,
|
| @@ -293,10 +325,16 @@ void UserCloudPolicyStoreChromeOS::OnLegacyLoadFinished(
|
| scoped_ptr<em::PolicyFetchResponse> policy) {
|
| status_ = status;
|
| if (policy.get()) {
|
| - Validate(policy.Pass(),
|
| - base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
|
| - weak_factory_.GetWeakPtr(),
|
| - dm_token, device_id));
|
| + // Create and configure a validator for the loaded legacy policy. Note that
|
| + // the signature on this policy is not verified.
|
| + scoped_ptr<UserCloudPolicyValidator> validator =
|
| + CreateValidator(policy.Pass());
|
| + validator->ValidateUsername(username_);
|
| + validator.release()->StartValidation(
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated,
|
| + weak_factory_.GetWeakPtr(),
|
| + dm_token,
|
| + device_id));
|
| } else {
|
| InstallLegacyTokens(dm_token, device_id);
|
| }
|
| @@ -309,8 +347,7 @@ void UserCloudPolicyStoreChromeOS::OnLegacyPolicyValidated(
|
| validation_status_ = validator->status();
|
| if (validator->success()) {
|
| status_ = STATUS_OK;
|
| - InstallPolicy(validator->policy_data().Pass(),
|
| - validator->payload().Pass());
|
| + InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
|
|
|
| // Clear the public key version. The public key version field would
|
| // otherwise indicate that we have key installed in the store when in fact
|
| @@ -348,4 +385,77 @@ void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
|
| LOG(ERROR) << "Failed to remove cache dir " << dir.value();
|
| }
|
|
|
| +void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
|
| + const base::Closure& callback) {
|
| + std::vector<uint8>* key = new std::vector<uint8>();
|
| + content::BrowserThread::PostBlockingPoolTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
|
| + policy_key_path_,
|
| + key),
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded,
|
| + weak_factory_.GetWeakPtr(),
|
| + base::Owned(key),
|
| + callback));
|
| +}
|
| +
|
| +// static
|
| +void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
|
| + std::vector<uint8>* key) {
|
| + if (!file_util::PathExists(path)) {
|
| + VLOG(1) << "No key at " << path.value();
|
| + return;
|
| + }
|
| +
|
| + int64 size;
|
| + if (!file_util::GetFileSize(path, &size)) {
|
| + LOG(ERROR) << "Could not get size of " << path.value();
|
| + } else if (size == 0 || size > kKeySizeLimit) {
|
| + LOG(ERROR) << "Key at " << path.value() << " has bad size " << size;
|
| + } else {
|
| + key->resize(size);
|
| + int read_size = file_util::ReadFile(
|
| + path, reinterpret_cast<char*>(vector_as_array(key)), size);
|
| + if (read_size != size) {
|
| + LOG(ERROR) << "Failed to read key at " << path.value();
|
| + key->clear();
|
| + }
|
| + }
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
|
| + std::vector<uint8>* key,
|
| + const base::Closure& callback) {
|
| + policy_key_.swap(*key);
|
| + policy_key_loaded_ = true;
|
| + callback.Run();
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::EnsurePolicyKeyLoaded(
|
| + const base::Closure& callback) {
|
| + if (policy_key_loaded_) {
|
| + callback.Run();
|
| + } else {
|
| + // Get the hashed username that's part of the key's path, to determine
|
| + // |policy_key_path_|.
|
| + cryptohome_client_->GetSanitizedUsername(username_,
|
| + base::Bind(&UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername,
|
| + weak_factory_.GetWeakPtr(),
|
| + callback));
|
| + }
|
| +}
|
| +
|
| +void UserCloudPolicyStoreChromeOS::OnGetSanitizedUsername(
|
| + const base::Closure& callback,
|
| + chromeos::DBusMethodCallStatus call_status,
|
| + const std::string& sanitized_username) {
|
| + // The default empty path will always yield an empty key.
|
| + if (call_status == chromeos::DBUS_METHOD_CALL_SUCCESS &&
|
| + !sanitized_username.empty()) {
|
| + policy_key_path_ = user_policy_key_dir_.Append(
|
| + base::StringPrintf(kPolicyKeyFile, sanitized_username.c_str()));
|
| + }
|
| + ReloadPolicyKey(callback);
|
| +}
|
| +
|
| } // namespace policy
|
|
|