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 |