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

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: Created 9 years, 6 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..b9d9a77e582a0ea7af472974fd3d5bcb78529afe
--- /dev/null
+++ b/chrome/browser/policy/cros_user_policy_cache.cc
@@ -0,0 +1,496 @@
+// 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/policy/cros_user_policy_identity_strategy.h"
+#include "chrome/browser/policy/proto/cloud_policy.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";
+
+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));
gfeher 2011/06/22 12:41:31 How about collapsing these arguments into fewer li
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
+}
+
+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]),
gfeher 2011/06/22 12:41:31 Nit: indent.
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
+ 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;
+
+ StorePolicyOperation(const em::PolicyFetchResponse& policy,
+ chromeos::LoginLibrary* login_library,
+ const StatusCallback& callback);
+
+ // Executes the operation.
+ void Run();
+
+ // Cancels the operation, making sure that any pending callbacks get killed.
+ void Cancel();
+
+ const em::PolicyFetchResponse& policy() { return policy_; }
+
+ private:
+ // A callback function suitable for passing to login_library.
+ static void StorePolicyCallback(void* delegate, bool result);
+
+ em::PolicyFetchResponse policy_;
+ chromeos::LoginLibrary* login_library_;
+ StatusCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(StorePolicyOperation);
+};
+
+CrosUserPolicyCache::StorePolicyOperation::StorePolicyOperation(
+ const em::PolicyFetchResponse& policy,
+ chromeos::LoginLibrary* login_library,
+ const StatusCallback& callback)
+ : policy_(policy),
+ login_library_(login_library),
+ callback_(callback) {}
+
+void CrosUserPolicyCache::StorePolicyOperation::Run() {
+ std::string serialized;
+ if (!policy_.SerializeToString(&serialized)) {
+ LOG(ERROR) << "Failed to serialize policy protobuf!";
+ callback_.Run(false);
+ delete this;
gfeher 2011/06/22 12:41:31 Here and all the similar cases: please make sure t
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
+ return;
+ }
+ login_library_->RequestStoreUserPolicy(serialized, StorePolicyCallback, this);
+}
+
+void CrosUserPolicyCache::StorePolicyOperation::Cancel() {
gfeher 2011/06/22 12:41:31 delete this?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Actually no, since the operation may be in flight.
+ callback_.Reset();
+}
+
+// static
+void CrosUserPolicyCache::StorePolicyOperation::StorePolicyCallback(
+ void* delegate,
+ bool result) {
+ scoped_ptr<StorePolicyOperation> op(
+ static_cast<StorePolicyOperation*>(delegate));
+ if (!op->callback_.is_null())
+ op->callback_.Run(result);
+}
+
+class CrosUserPolicyCache::RetrievePolicyOperation {
+ public:
+ typedef base::Callback<void(bool, const em::PolicyFetchResponse&)>
+ ResultCallback;
+
+ RetrievePolicyOperation(PolicyKey* key,
+ chromeos::LoginLibrary* login_library,
+ bool reload_key,
+ const ResultCallback& callback);
+
+ // Executes the operation.
+ void Run();
+
+ // Cancels the operation, disengaging all pending callbacks.
+ void Cancel();
+
+ private:
+ // 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_;
+ chromeos::LoginLibrary* login_library_;
+ bool reload_key_;
+ ResultCallback callback_;
+ em::PolicyFetchResponse policy_;
+
+ DISALLOW_COPY_AND_ASSIGN(RetrievePolicyOperation);
+};
+
+CrosUserPolicyCache::RetrievePolicyOperation::RetrievePolicyOperation(
+ PolicyKey* key,
+ chromeos::LoginLibrary* login_library,
+ bool reload_key,
+ const ResultCallback& callback)
+ : key_(key),
+ login_library_(login_library),
+ reload_key_(reload_key),
+ callback_(callback) {}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::Run() {
+ login_library_->RequestRetrieveUserPolicy(RetrievePolicyCallback, this);
+}
+
+void CrosUserPolicyCache::RetrievePolicyOperation::Cancel() {
gfeher 2011/06/22 12:41:31 delete this?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 See above.
+ callback_.Reset();
+}
+
+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) {
+ scoped_ptr<RetrievePolicyOperation> scoped_killer(this);
gfeher 2011/06/22 12:41:31 Please also try and implement this in a way that t
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Done.
+ if (!callback_.is_null())
+ callback_.Run(status, policy_);
+}
+
+// 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,
+ CrosUserPolicyIdentityStrategy* identity_strategy,
+ const FilePath& legacy_cache_file)
+ : key_(new PolicyKey(FilePath(kUserPolicyKeyFile))),
+ login_library_(login_library),
+ identity_strategy_(identity_strategy),
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)),
+ legacy_cache_file_(legacy_cache_file),
+ store_operation_(NULL),
+ retrieve_operation_(NULL) {
+ legacy_token_cache_ = new UserPolicyTokenCache(weak_ptr_factory_.GetWeakPtr(),
+ legacy_cache_file);
+}
+
+CrosUserPolicyCache::~CrosUserPolicyCache() {
+ CancelStore();
+ CancelRetrieve();
+}
+
+void CrosUserPolicyCache::Load() {
+ key_->LoadKey();
+ retrieve_operation_ =
+ new RetrievePolicyOperation(
+ key_,
+ login_library_,
+ false,
+ base::Bind(&CrosUserPolicyCache::OnPolicyLoadDone,
+ base::Unretained(this)));
+ retrieve_operation_->Run();
+}
+
+void CrosUserPolicyCache::SetPolicy(const em::PolicyFetchResponse& policy) {
+ CancelStore();
+ set_last_policy_refresh_time(base::Time::NowFromSystemTime());
+ store_operation_ =
+ new StorePolicyOperation(policy,
+ login_library_,
+ base::Bind(&CrosUserPolicyCache::OnPolicyStored,
+ base::Unretained(this)));
+ store_operation_->Run();
+}
+
+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(identity_strategy_->GetPolicyType());
+ 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::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::OnTokenCacheLoaded(const std::string& token,
+ const std::string& device_id) {
+ if (!token.empty() && !device_id.empty()) {
+ identity_strategy_->SetDeviceCredentials(device_id, token);
+ BrowserThread::PostTask(BrowserThread::FILE,
+ FROM_HERE,
+ NewRunnableFunction(&RemoveLegacyCacheDir,
+ legacy_cache_file_.DirName()));
+ } else {
+ identity_strategy_->EnableRegistration();
+ }
+}
+
+void CrosUserPolicyCache::OnPolicyStored(bool result) {
+ DCHECK(store_operation_);
+ if (result) {
gfeher 2011/06/22 12:41:31 So after storing a policy you immediately retrieve
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 Oh, the idea is to only have one read path and use
+ CancelRetrieve();
+ retrieve_operation_ =
+ new RetrievePolicyOperation(
+ key_,
+ login_library_,
+ store_operation_->policy().has_new_public_key(),
+ base::Bind(&CrosUserPolicyCache::OnPolicyReloadDone,
+ base::Unretained(this)));
+ retrieve_operation_->Run();
+ } 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 cache.
+ legacy_token_cache_->Load();
gfeher 2011/06/22 12:41:31 Do we get a SetPolicy callback if this succeeds?
Mattias Nissler (ping if slow) 2011/06/22 17:17:35 No, we just pull down policy again. I'm not trying
gfeher 2011/06/22 22:18:17 I was formulating my arguments against wasting tim
+ return;
+ }
+
+ 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);
+ identity_strategy_->EnableRegistration();
+ 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);
+ identity_strategy_->EnableRegistration();
+ return;
+ }
+ identity_strategy_->SetDeviceCredentials(policy_data.device_id(),
+ policy_data.request_token());
+ SetPolicyInternal(policy, NULL, true);
+}
+
+void CrosUserPolicyCache::OnPolicyReloadDone(
+ bool result,
+ const em::PolicyFetchResponse& policy) {
+ DCHECK(retrieve_operation_);
+ CancelRetrieve();
+ if (result) {
+ SetPolicyInternal(policy, NULL, false);
+ } 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