Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(110)

Unified Diff: chrome/browser/policy/cros_user_policy_cache.cc

Issue 7233006: Store/Retrieve CrOS user policy in session_manager. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: build fix Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/policy/cros_user_policy_cache.cc
diff --git a/chrome/browser/policy/cros_user_policy_cache.cc b/chrome/browser/policy/cros_user_policy_cache.cc
new file mode 100644
index 0000000000000000000000000000000000000000..752dd7c5d7e3f2b236a11c5ef4f492f00dd4616e
--- /dev/null
+++ b/chrome/browser/policy/cros_user_policy_cache.cc
@@ -0,0 +1,548 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/cros_user_policy_cache.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "chrome/browser/chromeos/cros/login_library.h"
+#include "chrome/browser/policy/proto/cloud_policy.pb.h"
+#include "chrome/browser/policy/proto/device_management_backend.pb.h"
+#include "chrome/browser/policy/proto/device_management_local.pb.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "content/browser/browser_thread.h"
+#include "crypto/signature_verifier.h"
+
+using extension_misc::kSignatureAlgorithm;
+
+static const FilePath::CharType kUserPolicyKeyFile[] =
+ "/home/chronos/user/.enterprise/key";
gfeher 2011/07/21 12:24:23 How about moving this to chrome/common/chrome_path
Mattias Nissler (ping if slow) 2011/07/21 14:20:25 Done.
+
+namespace policy {
+
+// Decodes a CloudPolicySettings object into two maps with mandatory and
+// recommended settings, respectively. The implementation is generated code
+// in policy/cloud_policy_generated.cc.
+void DecodePolicy(const em::CloudPolicySettings& policy,
+ PolicyMap* mandatory, PolicyMap* recommended);
+
+// Manages the policy key and implements signature checking on the key.
+class CrosUserPolicyCache::PolicyKey
+ : public base::RefCountedThreadSafe<PolicyKey> {
+ public:
+ typedef base::Callback<void(bool)> StatusCallback;
+
+ explicit PolicyKey(const FilePath& key_file);
+
+ // Loads the key asynchronously on the file thread.
+ void LoadKey();
+
+ // Checks whether |signature| is a valid over |data|. The status will be
+ // reported through |callback| once the check is complete. If the |reload_key|
+ // flag is set, the key is first reloaded from the file.
+ void CheckSignature(const std::string& data,
+ const std::string& signature,
+ bool reload_key,
+ const StatusCallback& callback);
+
+ private:
+ // Reports the result of a signature check. Must be called on the UI thread.
+ void ReportResult(const StatusCallback& callback, bool result);
+
+ // Read the public key from |key_file_|. Must be called on the file thread.
+ void ReadKeyFromFile();
+
+ // Runs the actual signature checking. Must be called on the file thread.
+ void CheckSignatureOnFileThread(const std::string& data,
+ const std::string& signature,
+ bool reload_key,
+ const StatusCallback& callback);
+
+ // The data members should only be accessed from the file thread.
+ const FilePath key_file_;
+ std::vector<uint8> public_key_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyKey);
+};
+
+CrosUserPolicyCache::PolicyKey::PolicyKey(const FilePath& key_file)
+ : key_file_(key_file) {}
+
+void CrosUserPolicyCache::PolicyKey::LoadKey() {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &PolicyKey::ReadKeyFromFile));
+}
+
+void CrosUserPolicyCache::PolicyKey::CheckSignature(
+ const std::string& data,
+ const std::string& signature,
+ bool reload_key,
+ const StatusCallback& callback) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &PolicyKey::CheckSignatureOnFileThread,
+ data, signature, reload_key, callback));
+}
+
+void CrosUserPolicyCache::PolicyKey::CheckSignatureOnFileThread(
+ const std::string& data,
+ const std::string& signature,
+ bool reload_key,
+ const StatusCallback& callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ bool status = false;
+
+ // Re-read the key if necessary.
+ if (public_key_.empty() || reload_key)
+ ReadKeyFromFile();
+
+ if (!public_key_.empty()) {
+ crypto::SignatureVerifier verifier;
+ if (verifier.VerifyInit(kSignatureAlgorithm, sizeof(kSignatureAlgorithm),
+ reinterpret_cast<const uint8*>(&signature[0]),
+ signature.size(),
+ &public_key_[0], public_key_.size())) {
+ verifier.VerifyUpdate(reinterpret_cast<const uint8*>(data.c_str()),
+ data.length());
+ status = verifier.VerifyFinal();
+ }
+ }
+
+ BrowserThread::PostTask(BrowserThread::UI,
+ FROM_HERE,
+ NewRunnableMethod(this, &PolicyKey::ReportResult,
+ callback, status));
+}
+
+void CrosUserPolicyCache::PolicyKey::ReportResult(
+ const StatusCallback& callback,
+ bool result) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ callback.Run(result);
+}
+
+void CrosUserPolicyCache::PolicyKey::ReadKeyFromFile() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ public_key_.clear();
+ if (!file_util::PathExists(key_file_))
+ return;
+
+ int64 size = -1;
+ if (!file_util::GetFileSize(key_file_, &size)) {
+ LOG(ERROR) << "Failed to get file size for " << key_file_.value();
+ return;
+ }
+
+ const int64 kMaxSize = 1 * 1024 * 1024; // 1M should be sufficient.
+ if (size < 0 || size > kMaxSize) {
+ LOG(ERROR) << "Bogus policy key file size " << size;
+ return;
+ }
+
+ std::vector<uint8> key_data(size);
+ if (!file_util::ReadFile(key_file_,
+ reinterpret_cast<char*>(&key_data[0]),
+ key_data.size())) {
+ LOG(ERROR) << "Failed to read " << key_file_.value();
+ return;
+ }
+
+ public_key_.swap(key_data);
+}
+
+// Takes care of sending a new policy blob to session manager and reports back
+// the status through a callback.
+class CrosUserPolicyCache::StorePolicyOperation {
+ public:
+ typedef base::Callback<void(bool)> StatusCallback;
+
+ // Creates and exectues an operation.
+ static StorePolicyOperation* Run(const em::PolicyFetchResponse& policy,
+ chromeos::LoginLibrary* login_library,
+ const StatusCallback& callback);
+
+ // Cancels the operation, making sure that any pending callbacks get killed.
+ void Cancel();
+
+ const em::PolicyFetchResponse& policy() { return policy_; }
+
+ private:
+ StorePolicyOperation(const em::PolicyFetchResponse& policy,
+ const StatusCallback& callback);
+
+ // StorePolicyOperation manages its own lifetime.
+ ~StorePolicyOperation() {}
+
+ // A callback function suitable for passing to login_library.
+ static void StorePolicyCallback(void* delegate, bool result);
+
+ em::PolicyFetchResponse policy_;
+ StatusCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
+};
+
+// static
+CrosUserPolicyCache::StorePolicyOperation*
+ CrosUserPolicyCache::StorePolicyOperation::Run(
+ const em::PolicyFetchResponse& policy,
+ chromeos::LoginLibrary* login_library,
+ const StatusCallback& callback) {
+ StorePolicyOperation* op = new StorePolicyOperation(policy, callback);
+ std::string serialized;
+ if (!policy.SerializeToString(&serialized)) {
+ LOG(ERROR) << "Failed to serialize policy protobuf!";
+ callback.Run(false);
+ delete op;
+ return NULL;
+ }
+ login_library->RequestStoreUserPolicy(serialized, StorePolicyCallback, op);
+ return op;
+}
+
+void CrosUserPolicyCache::StorePolicyOperation::Cancel() {
+ callback_.Reset();
+}
+
+CrosUserPolicyCache::StorePolicyOperation::StorePolicyOperation(
+ const em::PolicyFetchResponse& policy,
+ const StatusCallback& callback)
+ : policy_(policy),
+ callback_(callback) {}
+
+// static
+void CrosUserPolicyCache::StorePolicyOperation::StorePolicyCallback(
+ void* delegate,
+ bool result) {
+ StorePolicyOperation* op(static_cast<StorePolicyOperation*>(delegate));
+ if (!op->callback_.is_null())
+ op->callback_.Run(result);
+ delete op;
+}
+
+class CrosUserPolicyCache::RetrievePolicyOperation {
gfeher 2011/07/21 12:24:23 I took a look at DevicePolicyCache and it seems li
Mattias Nissler (ping if slow) 2011/07/21 14:20:25 I thought about that but rejected that idea for se
+ public:
+ typedef base::Callback<void(bool, const em::PolicyFetchResponse&)>
+ ResultCallback;
+
+ // Creates and executes an operation.
+ static RetrievePolicyOperation* Run(PolicyKey* key,
+ chromeos::LoginLibrary* login_library,
+ bool reload_key,
+ const ResultCallback& callback);
+
+ // Cancels the operation, disengaging all pending callbacks.
+ void Cancel();
+
+ private:
+ RetrievePolicyOperation(PolicyKey* key,
+ bool reload_key,
+ const ResultCallback& callback);
+
+ // RetrievePolicyOperation manages its own lifetime.
+ ~RetrievePolicyOperation() {}
+
+ // Decodes the policy data and triggers a signature check.
+ void OnPolicyRetrieved(const char* data, unsigned int size);
+
+ // Handles the signature verification callback and reports the result.
+ void OnSignatureChecked(bool result);
+
+ // Finishes the operation, makes the |callback_|, and deletes |this|.
+ void Finish(bool status);
+
+ // A callback function suitable for passing to login_library.
+ static void RetrievePolicyCallback(void* delegate,
+ const char* data,
+ unsigned int size);
+ scoped_refptr<PolicyKey> key_;
+ bool reload_key_;
+ ResultCallback callback_;
+ em::PolicyFetchResponse policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOperation);
+};
+
+// static
+CrosUserPolicyCache::RetrievePolicyOperation*
+ CrosUserPolicyCache::RetrievePolicyOperation::Run(
+ PolicyKey* key,
+ chromeos::LoginLibrary* login_library,
+ bool reload_key,
+ const ResultCallback& callback) {
+ RetrievePolicyOperation* op =
+ new RetrievePolicyOperation(key, reload_key, callback);
+ login_library->RequestRetrieveUserPolicy(RetrievePolicyCallback, op);
+ return op;
+}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::Cancel() {
+ callback_.Reset();
+}
+
+CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyOperation(
+ PolicyKey* key,
+ bool reload_key,
+ const ResultCallback& callback)
+ : key_(key),
+ reload_key_(reload_key),
+ callback_(callback) {}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::OnPolicyRetrieved(
+ const char* data,
+ unsigned int size) {
+ if (!policy_.ParseFromArray(data, size) ||
+ !policy_.has_policy_data() ||
+ !policy_.has_policy_data_signature()) {
+ LOG(ERROR) << "Failed to decode policy";
+ Finish(false);
+ return;
+ }
+ key_->CheckSignature(policy_.policy_data(),
+ policy_.policy_data_signature(),
+ reload_key_,
+ base::Bind(&RetrievePolicyOperation::OnSignatureChecked,
+ base::Unretained(this)));
+}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::OnSignatureChecked(
+ bool result) {
+ if (!result)
+ LOG(ERROR) << "User policy signature check failed.";
+ Finish(result);
+}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::Finish(bool status) {
+ if (!callback_.is_null())
+ callback_.Run(status, policy_);
+ delete this;
+}
+
+// static
+void CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyCallback(
+ void *delegate,
+ const char* data,
+ unsigned int size) {
+ static_cast<RetrievePolicyOperation*>(delegate)->OnPolicyRetrieved(data,
+ size);
+}
+
+CrosUserPolicyCache::CrosUserPolicyCache(
+ chromeos::LoginLibrary* login_library,
+ CloudPolicyDataStore* data_store,
+ const FilePath& legacy_token_cache_file,
+ const FilePath& legacy_policy_cache_file)
+ : key_(new PolicyKey(FilePath(kUserPolicyKeyFile))),
+ login_library_(login_library),
+ data_store_(data_store),
+ policy_cache_loaded_(false),
+ store_operation_(NULL),
+ retrieve_operation_(NULL),
+ legacy_cache_dir_(legacy_token_cache_file.DirName()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ legacy_token_cache_delegate_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ legacy_policy_cache_delegate_factory_(this)) {
+ DCHECK_EQ(legacy_token_cache_file.DirName().value(),
+ legacy_policy_cache_file.DirName().value());
+ legacy_token_loader_ =
+ new UserPolicyTokenLoader(
+ legacy_token_cache_delegate_factory_.GetWeakPtr(),
+ legacy_token_cache_file);
+ legacy_policy_cache_ =
+ new UserPolicyDiskCache(
+ legacy_policy_cache_delegate_factory_.GetWeakPtr(),
+ legacy_policy_cache_file);
+}
+
+CrosUserPolicyCache::~CrosUserPolicyCache() {
+ CancelStore();
+ CancelRetrieve();
+}
+
+void CrosUserPolicyCache::Load() {
+ key_->LoadKey();
+ retrieve_operation_ =
+ RetrievePolicyOperation::Run(
+ key_,
+ login_library_,
+ false,
+ base::Bind(&CrosUserPolicyCache::OnPolicyLoadDone,
+ base::Unretained(this)));
+}
+
+void CrosUserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
+ CancelStore();
+ set_last_policy_refresh_time(base::Time::NowFromSystemTime());
+ store_operation_ =
+ StorePolicyOperation::Run(policy,
+ login_library_,
+ base::Bind(&CrosUserPolicyCache::OnPolicyStored,
+ base::Unretained(this)));
+}
+
+void CrosUserPolicyCache::SetUnmanaged() {
+ base::Time now(base::Time::NowFromSystemTime());
+ SetUnmanagedInternal(now);
+
+ // Construct a policy blob with unmanaged state.
+ em::PolicyData policy_data;
+ policy_data.set_policy_type(data_store_->policy_type());
+ policy_data.set_timestamp((now - base::Time::UnixEpoch()).InMilliseconds());
+ policy_data.set_state(em::PolicyData::UNMANAGED);
+
+ em::PolicyFetchResponse policy;
+ if (!policy_data.SerializeToString(policy.mutable_policy_data())) {
+ LOG(ERROR) << "Failed to serialize policy_data";
+ return;
+ }
+
+ SetPolicy(policy);
+}
+
+bool CrosUserPolicyCache::IsReady() {
+ return policy_cache_loaded_;
+}
+
+bool CrosUserPolicyCache::DecodePolicyData(const em::PolicyData& policy_data,
+ PolicyMap* mandatory,
+ PolicyMap* recommended) {
+ em::CloudPolicySettings policy;
+ if (!policy.ParseFromString(policy_data.policy_value())) {
+ LOG(WARNING) << "Failed to parse CloudPolicySettings protobuf.";
+ return false;
+ }
+ DecodePolicy(policy, mandatory, recommended);
+ return true;
+}
+
+void CrosUserPolicyCache::OnTokenLoaded(const std::string& token,
+ const std::string& device_id) {
+ data_store_->set_device_id(device_id);
+ data_store_->SetDeviceToken(token, true);
+}
+
+void CrosUserPolicyCache::OnDiskCacheLoaded(
+ const em::CachedCloudPolicyResponse& policy) {
+ policy_cache_loaded_ = true;
+ if (policy.unmanaged()) {
+ SetUnmanagedInternal(base::Time::FromTimeT(policy.timestamp()));
+ } else if (policy.has_cloud_policy()) {
+ base::Time timestamp;
+ if (SetPolicyInternal(policy.cloud_policy(), &timestamp, true))
+ set_last_policy_refresh_time(timestamp);
+ }
+}
+
+void CrosUserPolicyCache::OnPolicyStored(bool result) {
+ DCHECK(store_operation_);
+ if (result) {
+ // Policy is stored successfully, reload from session_manager and apply.
+ // This helps us making sure we only use policy that session_manager has
+ // checked and confirmed to be good.
+ CancelRetrieve();
+ retrieve_operation_ =
+ RetrievePolicyOperation::Run(
+ key_,
+ login_library_,
+ store_operation_->policy().has_new_public_key(),
+ base::Bind(&CrosUserPolicyCache::OnPolicyReloadDone,
+ base::Unretained(this)));
+
+ // Now that we have installed the new policy blob, we can make sure that the
+ // old cache directory is removed.
+ if (!legacy_cache_dir_.empty()) {
+ BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
+ NewRunnableFunction(&RemoveLegacyCacheDir,
+ legacy_cache_dir_));
+ }
+ } else {
+ InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
+ CloudPolicySubsystem::POLICY_LOCAL_ERROR);
+ }
+ CancelStore();
+}
+
+void CrosUserPolicyCache::OnPolicyLoadDone(
+ bool result,
+ const em::PolicyFetchResponse& policy) {
+ DCHECK(retrieve_operation_);
+ CancelRetrieve();
+ if (!result) {
+ // No policy present. Try to load the legacy token and policy cache.
+ legacy_token_loader_->Load();
+ legacy_policy_cache_->Load();
+ return;
+ }
+
+ policy_cache_loaded_ = true;
+
+ // We have new-style policy, no need to clean up.
+ legacy_cache_dir_.clear();
+
+ em::PolicyData policy_data;
+ if (!policy_data.ParseFromString(policy.policy_data())) {
+ LOG(WARNING) << "Failed to parse PolicyData protobuf.";
+ InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
+ CloudPolicySubsystem::POLICY_LOCAL_ERROR);
+ data_store_->SetDeviceToken(std::string(), true);
+ return;
+ }
+ if (policy_data.request_token().empty() ||
+ policy_data.username().empty() ||
+ policy_data.device_id().empty()) {
+ LOG(WARNING) << "Policy protobuf is missing credentials";
+ InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
+ CloudPolicySubsystem::POLICY_LOCAL_ERROR);
+ data_store_->SetDeviceToken(std::string(), true);
+ return;
+ }
+ data_store_->set_device_id(policy_data.device_id());
+ data_store_->SetDeviceToken(policy_data.request_token(), true);
+ if (SetPolicyInternal(policy, NULL, true))
+ set_last_policy_refresh_time(base::Time::NowFromSystemTime());
+}
+
+void CrosUserPolicyCache::OnPolicyReloadDone(
+ bool result,
+ const em::PolicyFetchResponse& policy) {
+ DCHECK(retrieve_operation_);
+ policy_cache_loaded_ = true;
+ CancelRetrieve();
+ if (result) {
+ if (SetPolicyInternal(policy, NULL, false))
+ set_last_policy_refresh_time(base::Time::NowFromSystemTime());
+ } else {
+ InformNotifier(CloudPolicySubsystem::LOCAL_ERROR,
+ CloudPolicySubsystem::POLICY_LOCAL_ERROR);
+ }
+}
+
+void CrosUserPolicyCache::CancelStore() {
+ if (store_operation_) {
+ store_operation_->Cancel();
+ store_operation_ = NULL;
+ }
+}
+
+void CrosUserPolicyCache::CancelRetrieve() {
+ if (retrieve_operation_) {
+ retrieve_operation_->Cancel();
+ retrieve_operation_ = NULL;
+ }
+}
+
+// static
+void CrosUserPolicyCache::RemoveLegacyCacheDir(const FilePath& dir) {
+ if (!file_util::Delete(dir, true))
+ PLOG(ERROR) << "Failed to remove " << dir.value();
+}
+
+} // namespace policy

Powered by Google App Engine
This is Rietveld 408576698