summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-02 17:28:39 +0000
committeratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-02 17:28:39 +0000
commit1b9a4a1237bed2d1cf28ffb5e40f7154d8542d4e (patch)
treefb62afafcdd2dd405e63fe409e491159d3094dd7
parent5612c2ae47ca0145af9e7adc40ead427ee7196c7 (diff)
downloadchromium_src-1b9a4a1237bed2d1cf28ffb5e40f7154d8542d4e.zip
chromium_src-1b9a4a1237bed2d1cf28ffb5e40f7154d8542d4e.tar.gz
chromium_src-1b9a4a1237bed2d1cf28ffb5e40f7154d8542d4e.tar.bz2
Added support for signed policy blobs on desktop.
PolicyFetchRequests on desktop now request signed policy blobs. Also added plumbing for injecting a google-supplied key to verify the blob signing keys to protect against local tampering. BUG=275291 TBR=joaodasilva@chromium.org Review URL: https://codereview.chromium.org/116273002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248416 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--build/ios/grit_whitelist.txt1
-rw-r--r--chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc1
-rw-r--r--chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc7
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_service.cc1
-rw-r--r--chrome/browser/chromeos/policy/device_local_account_policy_store.cc5
-rw-r--r--chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc2
-rw-r--r--chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc3
-rw-r--r--chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc40
-rw-r--r--chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h6
-rw-r--r--chrome/browser/chromeos/settings/device_settings_service.h7
-rw-r--r--chrome/browser/chromeos/settings/session_manager_operation.cc5
-rw-r--r--chrome/browser/chromeos/settings/session_manager_operation_unittest.cc11
-rw-r--r--chrome/browser/policy/cloud/policy_header_service_factory.cc6
-rw-r--r--chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc5
-rw-r--r--components/policy.gypi1
-rw-r--r--components/policy/core/DEPS1
-rw-r--r--components/policy/core/browser/cloud/message_util.cc4
-rw-r--r--components/policy/core/common/cloud/cloud_policy_client.cc12
-rw-r--r--components/policy/core/common/cloud/cloud_policy_client.h4
-rw-r--r--components/policy/core/common/cloud/cloud_policy_client_unittest.cc6
-rw-r--r--components/policy/core/common/cloud/cloud_policy_constants.cc51
-rw-r--r--components/policy/core/common/cloud/cloud_policy_constants.h5
-rw-r--r--components/policy/core/common/cloud/cloud_policy_validator.cc114
-rw-r--r--components/policy/core/common/cloud/cloud_policy_validator.h46
-rw-r--r--components/policy/core/common/cloud/cloud_policy_validator_unittest.cc19
-rw-r--r--components/policy/core/common/cloud/mock_cloud_policy_client.cc1
-rw-r--r--components/policy/core/common/cloud/mock_user_cloud_policy_store.cc2
-rw-r--r--components/policy/core/common/cloud/policy_builder.cc23
-rw-r--r--components/policy/core/common/cloud/policy_builder.h10
-rw-r--r--components/policy/core/common/cloud/policy_header_service.cc21
-rw-r--r--components/policy/core/common/cloud/policy_header_service.h4
-rw-r--r--components/policy/core/common/cloud/policy_header_service_unittest.cc7
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_manager.cc1
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_manager.h3
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store.cc189
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store.h23
-rw-r--r--components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc222
-rw-r--r--components/policy/core/common/policy_switches.cc3
-rw-r--r--components/policy/core/common/policy_switches.h1
-rw-r--r--components/policy/proto/device_management_backend.proto28
-rw-r--r--components/policy/proto/policy_signing_key.proto20
-rw-r--r--components/policy_strings.grdp3
42 files changed, 777 insertions, 147 deletions
diff --git a/build/ios/grit_whitelist.txt b/build/ios/grit_whitelist.txt
index ba61f04..c5dcff8 100644
--- a/build/ios/grit_whitelist.txt
+++ b/build/ios/grit_whitelist.txt
@@ -735,6 +735,7 @@ IDS_POLICY_STORE_STATUS_VALIDATION_ERROR
IDS_POLICY_SUBKEY_ERROR
IDS_POLICY_TYPE_ERROR
IDS_POLICY_VALIDATION_BAD_INITIAL_SIGNATURE
+IDS_POLICY_VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE
IDS_POLICY_VALIDATION_BAD_SIGNATURE
IDS_POLICY_VALIDATION_BAD_TIMESTAMP
IDS_POLICY_VALIDATION_BAD_USERNAME
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
index c472757..aa86001 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -251,6 +251,7 @@ scoped_ptr<CloudPolicyClient> DeviceCloudPolicyManagerChromeOS::CreateClient() {
return make_scoped_ptr(
new CloudPolicyClient(GetMachineID(), GetMachineModel(),
+ kPolicyVerificationKeyHash,
USER_AFFILIATION_NONE,
device_status_provider_.get(),
device_management_service_,
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
index 16468bb..a4a2e99 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.cc
@@ -46,7 +46,10 @@ void DeviceCloudPolicyStoreChromeOS::Store(
}
scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
- validator->ValidateSignature(*owner_key->public_key(), true);
+ validator->ValidateSignature(owner_key->public_key_as_string(),
+ GetPolicyVerificationKey(),
+ std::string(),
+ true);
validator->ValidateAgainstCurrentPolicy(
device_settings_service_->policy_data(),
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED,
@@ -74,7 +77,7 @@ void DeviceCloudPolicyStoreChromeOS::InstallInitialPolicy(
}
scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
- validator->ValidateInitialKey();
+ validator->ValidateInitialKey(GetPolicyVerificationKey());
validator.release()->StartValidation(
base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
index ed3546b..ef4b832 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_service.cc
@@ -65,6 +65,7 @@ scoped_ptr<CloudPolicyClient> CreateClient(
scoped_ptr<CloudPolicyClient> client(
new CloudPolicyClient(std::string(), std::string(),
+ kPolicyVerificationKeyHash,
USER_AFFILIATION_MANAGED,
NULL, device_management_service, request_context));
client->SetupRegistration(policy_data->request_token(),
diff --git a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
index 327b11e..fa13d73 100644
--- a/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
+++ b/chrome/browser/chromeos/policy/device_local_account_policy_store.cc
@@ -190,7 +190,10 @@ void DeviceLocalAccountPolicyStore::Validate(
: CloudPolicyValidatorBase::TIMESTAMP_NOT_REQUIRED,
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePayload();
- validator->ValidateSignature(*key->public_key(), false);
+ validator->ValidateSignature(key->public_key_as_string(),
+ GetPolicyVerificationKey(),
+ std::string(),
+ false);
validator.release()->StartValidation(callback);
}
diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
index 56b013c..3c25bac 100644
--- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
@@ -115,7 +115,7 @@ void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
validator->ValidatePayload();
- validator->ValidateInitialKey();
+ validator->ValidateInitialKey(GetPolicyVerificationKey());
validator.release()->StartValidation(
base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated,
weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index b2abb55..83d5954 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -119,7 +119,8 @@ void UserCloudPolicyManagerChromeOS::Connect(
device_management_service->GetServerUrl())));
}
scoped_ptr<CloudPolicyClient> cloud_policy_client(
- new CloudPolicyClient(std::string(), std::string(), user_affiliation,
+ new CloudPolicyClient(std::string(), std::string(),
+ kPolicyVerificationKeyHash, user_affiliation,
NULL, device_management_service,
request_context));
core()->Connect(cloud_policy_client.Pass());
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
index 03d949b..467aec7 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
@@ -18,6 +18,7 @@
#include "chrome/browser/chromeos/policy/user_policy_token_loader.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/session_manager_client.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "policy/proto/cloud_policy.pb.h"
#include "policy/proto/device_management_local.pb.h"
@@ -255,7 +256,11 @@ void UserCloudPolicyStoreChromeOS::LoadImmediately() {
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
validator->ValidateUsername(username_);
const bool allow_rotation = false;
- validator->ValidateSignature(policy_key_, allow_rotation);
+ validator->ValidateSignature(
+ policy_key_,
+ GetPolicyVerificationKey(),
+ std::string(), // No signature verification needed.
+ allow_rotation);
validator->RunValidation();
OnRetrievedPolicyValidated(validator.get());
}
@@ -268,10 +273,13 @@ void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
validator->ValidateUsername(username_);
if (policy_key_.empty()) {
- validator->ValidateInitialKey();
+ validator->ValidateInitialKey(GetPolicyVerificationKey());
} else {
const bool allow_rotation = true;
- validator->ValidateSignature(policy_key_, allow_rotation);
+ validator->ValidateSignature(policy_key_,
+ GetPolicyVerificationKey(),
+ std::string(),
+ allow_rotation);
}
// Start validation. The Validator will delete itself once validation is
@@ -288,7 +296,7 @@ void UserCloudPolicyStoreChromeOS::OnPolicyToStoreValidated(
UMA_HISTOGRAM_ENUMERATION(
"Enterprise.UserPolicyValidationStoreStatus",
validation_status_,
- UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
+ UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
if (!validator->success()) {
status_ = STATUS_VALIDATION_ERROR;
@@ -367,7 +375,10 @@ void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
validator->ValidateUsername(username_);
const bool allow_rotation = false;
- validator->ValidateSignature(policy_key_, allow_rotation);
+ validator->ValidateSignature(policy_key_,
+ GetPolicyVerificationKey(),
+ std::string(),
+ allow_rotation);
// Start validation. The Validator will delete itself once validation is
// complete.
validator.release()->StartValidation(
@@ -382,7 +393,7 @@ void UserCloudPolicyStoreChromeOS::OnRetrievedPolicyValidated(
UMA_HISTOGRAM_ENUMERATION(
"Enterprise.UserPolicyValidationLoadStatus",
validation_status_,
- UserCloudPolicyValidator::VALIDATION_POLICY_PARSE_ERROR + 1);
+ UserCloudPolicyValidator::VALIDATION_STATUS_SIZE);
if (!validator->success()) {
status_ = STATUS_VALIDATION_ERROR;
@@ -475,7 +486,7 @@ void UserCloudPolicyStoreChromeOS::RemoveLegacyCacheDir(
void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
const base::Closure& callback) {
- std::vector<uint8>* key = new std::vector<uint8>();
+ std::string* key = new std::string();
background_task_runner()->PostTaskAndReply(
FROM_HERE,
base::Bind(&UserCloudPolicyStoreChromeOS::LoadPolicyKey,
@@ -489,7 +500,7 @@ void UserCloudPolicyStoreChromeOS::ReloadPolicyKey(
// static
void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
- std::vector<uint8>* key) {
+ std::string* key) {
if (!base::PathExists(path)) {
// There is no policy key the first time that a user fetches policy. If
// |path| does not exist then that is the most likely scenario, so there's
@@ -499,17 +510,18 @@ void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
}
int64 size;
+ key->clear();
if (!base::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 = base::ReadFile(
- path, reinterpret_cast<char*>(vector_as_array(key)), size);
+ char buf[size];
+ int read_size = base::ReadFile(path, buf, size);
if (read_size != size) {
LOG(ERROR) << "Failed to read key at " << path.value();
- key->clear();
+ } else {
+ key->append(buf, size);
}
}
@@ -518,9 +530,9 @@ void UserCloudPolicyStoreChromeOS::LoadPolicyKey(const base::FilePath& path,
}
void UserCloudPolicyStoreChromeOS::OnPolicyKeyReloaded(
- std::vector<uint8>* key,
+ std::string* key,
const base::Closure& callback) {
- policy_key_.swap(*key);
+ policy_key_ = *key;
policy_key_loaded_ = true;
callback.Run();
}
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h
index 1e78869..36542cd 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.h
@@ -105,10 +105,10 @@ class UserCloudPolicyStoreChromeOS : public UserCloudPolicyStoreBase {
// Reads the contents of |path| into |key|.
static void LoadPolicyKey(const base::FilePath& path,
- std::vector<uint8>* key);
+ std::string* key);
// Callback for the key reloading.
- void OnPolicyKeyReloaded(std::vector<uint8>* key,
+ void OnPolicyKeyReloaded(std::string* key,
const base::Closure& callback);
// Invokes |callback| after creating |policy_key_|, if it hasn't been created
@@ -135,7 +135,7 @@ class UserCloudPolicyStoreChromeOS : public UserCloudPolicyStoreBase {
bool policy_key_loaded_;
base::FilePath policy_key_path_;
- std::vector<uint8> policy_key_;
+ std::string policy_key_;
DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStoreChromeOS);
};
diff --git a/chrome/browser/chromeos/settings/device_settings_service.h b/chrome/browser/chromeos/settings/device_settings_service.h
index 051d831..7e77c3e 100644
--- a/chrome/browser/chromeos/settings/device_settings_service.h
+++ b/chrome/browser/chromeos/settings/device_settings_service.h
@@ -15,6 +15,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
+#include "base/stl_util.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/tpm_token_loader.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
@@ -46,6 +47,12 @@ class OwnerKey : public base::RefCountedThreadSafe<OwnerKey> {
const std::vector<uint8>* public_key() {
return public_key_.get();
}
+
+ std::string public_key_as_string() {
+ return std::string(reinterpret_cast<const char*>(
+ vector_as_array(public_key_.get())), public_key_->size());
+ }
+
crypto::RSAPrivateKey* private_key() {
return private_key_.get();
}
diff --git a/chrome/browser/chromeos/settings/session_manager_operation.cc b/chrome/browser/chromeos/settings/session_manager_operation.cc
index e075a33..d7f994c 100644
--- a/chrome/browser/chromeos/settings/session_manager_operation.cc
+++ b/chrome/browser/chromeos/settings/session_manager_operation.cc
@@ -182,7 +182,10 @@ void SessionManagerOperation::ValidateDeviceSettings(
policy::CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED);
validator->ValidatePolicyType(policy::dm_protocol::kChromeDevicePolicyType);
validator->ValidatePayload();
- validator->ValidateSignature(*owner_key_->public_key(), false);
+ validator->ValidateSignature(owner_key_->public_key_as_string(),
+ policy::GetPolicyVerificationKey(),
+ std::string(),
+ false);
validator->StartValidation(
base::Bind(&SessionManagerOperation::ReportValidatorStatus,
weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc b/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc
index 980399c..40a434b 100644
--- a/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc
+++ b/chrome/browser/chromeos/settings/session_manager_operation_unittest.cc
@@ -263,7 +263,16 @@ TEST_F(SessionManagerOperationTest, SignAndStoreSettings) {
validator->ValidatePayload();
std::vector<uint8> public_key;
policy_.GetSigningKey()->ExportPublicKey(&public_key);
- validator->ValidateSignature(public_key, false);
+ // Convert from bytes to string format (which is what ValidateSignature()
+ // takes).
+ std::string public_key_as_string = std::string(
+ reinterpret_cast<const char*>(vector_as_array(&public_key)),
+ public_key.size());
+ validator->ValidateSignature(
+ public_key_as_string,
+ policy::GetPolicyVerificationKey(),
+ policy::PolicyBuilder::GetTestSigningKeySignature(),
+ false);
validator->StartValidation(
base::Bind(&SessionManagerOperationTest::CheckSuccessfulValidation,
base::Unretained(this)));
diff --git a/chrome/browser/policy/cloud/policy_header_service_factory.cc b/chrome/browser/policy/cloud/policy_header_service_factory.cc
index 7395ae5..c7ce7fa 100644
--- a/chrome/browser/policy/cloud/policy_header_service_factory.cc
+++ b/chrome/browser/policy/cloud/policy_header_service_factory.cc
@@ -104,8 +104,10 @@ PolicyHeaderServiceFactory::BuildServiceInstanceFor(
#endif
scoped_ptr<PolicyHeaderService> service = make_scoped_ptr(
- new PolicyHeaderService(
- device_management_service->GetServerUrl(), user_store, device_store));
+ new PolicyHeaderService(device_management_service->GetServerUrl(),
+ kPolicyVerificationKeyHash,
+ user_store,
+ device_store));
return new PolicyHeaderServiceWrapper(service.Pass());
}
diff --git a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc
index 2c56cc4..517e339 100644
--- a/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc
+++ b/chrome/browser/policy/cloud/user_cloud_policy_manager_factory.cc
@@ -13,6 +13,7 @@
#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
#include "components/policy/core/common/cloud/cloud_external_data_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
#include "components/policy/core/common/cloud/user_cloud_policy_store.h"
#include "content/public/browser/browser_context.h"
@@ -143,7 +144,9 @@ UserCloudPolicyManagerFactory::CreateManagerForOriginalBrowserContext(
DCHECK(!testing_factory_);
scoped_ptr<UserCloudPolicyStore> store(
- UserCloudPolicyStore::Create(context->GetPath(), background_task_runner));
+ UserCloudPolicyStore::Create(context->GetPath(),
+ GetPolicyVerificationKey(),
+ background_task_runner));
if (force_immediate_load)
store->LoadImmediately();
diff --git a/components/policy.gypi b/components/policy.gypi
index b70e547..ce934ac 100644
--- a/components/policy.gypi
+++ b/components/policy.gypi
@@ -199,6 +199,7 @@
'policy/proto/chrome_extension_policy.proto',
'policy/proto/device_management_backend.proto',
'policy/proto/device_management_local.proto',
+ 'policy/proto/policy_signing_key.proto',
],
'variables': {
'proto_in_dir': 'policy/proto',
diff --git a/components/policy/core/DEPS b/components/policy/core/DEPS
index 7b46c50..678a142 100644
--- a/components/policy/core/DEPS
+++ b/components/policy/core/DEPS
@@ -8,6 +8,7 @@ include_rules = [
# components/policy/resources/policy_templates.json.
"+policy/policy_constants.h",
"+policy/proto/chrome_extension_policy.pb.h",
+ "+policy/proto/policy_signing_key.pb.h",
"+policy/proto/cloud_policy.pb.h",
"+policy/proto/device_management_backend.pb.h",
]
diff --git a/components/policy/core/browser/cloud/message_util.cc b/components/policy/core/browser/cloud/message_util.cc
index ea5aa03..f394762 100644
--- a/components/policy/core/browser/cloud/message_util.cc
+++ b/components/policy/core/browser/cloud/message_util.cc
@@ -73,6 +73,10 @@ int GetIDSForValidationStatus(CloudPolicyValidatorBase::Status status) {
return IDS_POLICY_VALIDATION_BAD_USERNAME;
case CloudPolicyValidatorBase::VALIDATION_POLICY_PARSE_ERROR:
return IDS_POLICY_VALIDATION_POLICY_PARSE_ERROR;
+ case CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE:
+ return IDS_POLICY_VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
+ case CloudPolicyValidatorBase::VALIDATION_STATUS_SIZE:
+ NOTREACHED();
}
NOTREACHED() << "Unhandled validation status " << status;
return IDS_POLICY_VALIDATION_UNKNOWN_ERROR;
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index efd95f2..c090d50 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -50,12 +50,14 @@ CloudPolicyClient::StatusProvider::~StatusProvider() {}
CloudPolicyClient::CloudPolicyClient(
const std::string& machine_id,
const std::string& machine_model,
+ const std::string& verification_key_hash,
UserAffiliation user_affiliation,
StatusProvider* status_provider,
DeviceManagementService* service,
scoped_refptr<net::URLRequestContextGetter> request_context)
: machine_id_(machine_id),
machine_model_(machine_model),
+ verification_key_hash_(verification_key_hash),
user_affiliation_(user_affiliation),
device_mode_(DEVICE_MODE_NOT_SET),
submit_machine_id_(false),
@@ -161,16 +163,14 @@ void CloudPolicyClient::FetchPolicy() {
if (!it->second.empty())
fetch_request->set_settings_entity_id(it->second);
-#if defined(OS_CHROMEOS)
- // All policy types on ChromeOS ask for a signed policy blob.
+ // Request signed policy blobs to help prevent tampering on the client.
fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA);
-#else
- // Don't request signed blobs for desktop policy.
- fetch_request->set_signature_type(em::PolicyFetchRequest::NONE);
-#endif
if (public_key_version_valid_)
fetch_request->set_public_key_version(public_key_version_);
+ if (!verification_key_hash_.empty())
+ fetch_request->set_verification_key_hash(verification_key_hash_);
+
// These fields are included only in requests for chrome policy.
if (IsChromePolicy(it->first)) {
if (submit_machine_id_ && !machine_id_.empty())
diff --git a/components/policy/core/common/cloud/cloud_policy_client.h b/components/policy/core/common/cloud/cloud_policy_client.h
index c768165..fdd6a6d 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.h
+++ b/components/policy/core/common/cloud/cloud_policy_client.h
@@ -87,9 +87,12 @@ class POLICY_EXPORT CloudPolicyClient {
// |provider| and |service| are weak pointers and it's the caller's
// responsibility to keep them valid for the lifetime of CloudPolicyClient.
+ // |verification_key_hash| contains an identifier telling the DMServer which
+ // verification key to use.
CloudPolicyClient(
const std::string& machine_id,
const std::string& machine_model,
+ const std::string& verification_key_hash,
UserAffiliation user_affiliation,
StatusProvider* provider,
DeviceManagementService* service,
@@ -255,6 +258,7 @@ class POLICY_EXPORT CloudPolicyClient {
// Data necessary for constructing policy requests.
const std::string machine_id_;
const std::string machine_model_;
+ const std::string verification_key_hash_;
const UserAffiliation user_affiliation_;
NamespaceSet namespaces_to_fetch_;
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index 64666ee..8502c38 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -83,11 +83,8 @@ class CloudPolicyClientTest : public testing::Test {
em::PolicyFetchRequest* policy_fetch_request =
policy_request_.mutable_policy_request()->add_request();
policy_fetch_request->set_policy_type(dm_protocol::kChromeUserPolicyType);
-#if defined(OS_CHROMEOS)
policy_fetch_request->set_signature_type(em::PolicyFetchRequest::SHA1_RSA);
-#else
- policy_fetch_request->set_signature_type(em::PolicyFetchRequest::NONE);
-#endif
+ policy_fetch_request->set_verification_key_hash(kPolicyVerificationKeyHash);
policy_response_.mutable_policy_response()->add_response()->set_policy_data(
CreatePolicyData("fake-policy-data"));
@@ -122,6 +119,7 @@ class CloudPolicyClientTest : public testing::Test {
request_context_ = new net::TestURLRequestContextGetter(
loop_.message_loop_proxy());
client_.reset(new CloudPolicyClient(kMachineID, kMachineModel,
+ kPolicyVerificationKeyHash,
user_affiliation, &status_provider_,
&service_,
request_context_));
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc
index c88f1fa..56b9411 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -4,6 +4,7 @@
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "base/basictypes.h"
#include "base/command_line.h"
#include "components/policy/core/common/policy_switches.h"
@@ -49,6 +50,56 @@ const char kChromeExtensionPolicyType[] = "google/chrome/extension";
} // namespace dm_protocol
+#if !defined(OS_CHROMEOS)
+const uint8 kPolicyVerificationKey[] = {
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82,
+ 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xA7, 0xB3, 0xF9, 0x0D, 0xC7, 0xC7,
+ 0x8D, 0x84, 0x3D, 0x4B, 0x80, 0xDD, 0x9A, 0x2F, 0xF8, 0x69, 0xD4, 0xD1, 0x14,
+ 0x5A, 0xCA, 0x04, 0x4B, 0x1C, 0xBC, 0x28, 0xEB, 0x5E, 0x10, 0x01, 0x36, 0xFD,
+ 0x81, 0xEB, 0xE4, 0x3C, 0x16, 0x40, 0xA5, 0x8A, 0xE6, 0x08, 0xEE, 0xEF, 0x39,
+ 0x1F, 0x6B, 0x10, 0x29, 0x50, 0x84, 0xCE, 0xEE, 0x33, 0x5C, 0x48, 0x4A, 0x33,
+ 0xB0, 0xC8, 0x8A, 0x66, 0x0D, 0x10, 0x11, 0x9D, 0x6B, 0x55, 0x4C, 0x9A, 0x62,
+ 0x40, 0x9A, 0xE2, 0xCA, 0x21, 0x01, 0x1F, 0x10, 0x1E, 0x7B, 0xC6, 0x89, 0x94,
+ 0xDA, 0x39, 0x69, 0xBE, 0x27, 0x28, 0x50, 0x5E, 0xA2, 0x55, 0xB9, 0x12, 0x3C,
+ 0x79, 0x6E, 0xDF, 0x24, 0xBF, 0x34, 0x88, 0xF2, 0x5E, 0xD0, 0xC4, 0x06, 0xEE,
+ 0x95, 0x6D, 0xC2, 0x14, 0xBF, 0x51, 0x7E, 0x3F, 0x55, 0x10, 0x85, 0xCE, 0x33,
+ 0x8F, 0x02, 0x87, 0xFC, 0xD2, 0xDD, 0x42, 0xAF, 0x59, 0xBB, 0x69, 0x3D, 0xBC,
+ 0x77, 0x4B, 0x3F, 0xC7, 0x22, 0x0D, 0x5F, 0x72, 0xC7, 0x36, 0xB6, 0x98, 0x3D,
+ 0x03, 0xCD, 0x2F, 0x68, 0x61, 0xEE, 0xF4, 0x5A, 0xF5, 0x07, 0xAE, 0xAE, 0x79,
+ 0xD1, 0x1A, 0xB2, 0x38, 0xE0, 0xAB, 0x60, 0x5C, 0x0C, 0x14, 0xFE, 0x44, 0x67,
+ 0x2C, 0x8A, 0x08, 0x51, 0x9C, 0xCD, 0x3D, 0xDB, 0x13, 0x04, 0x57, 0xC5, 0x85,
+ 0xB6, 0x2A, 0x0F, 0x02, 0x46, 0x0D, 0x2D, 0xCA, 0xE3, 0x3F, 0x84, 0x9E, 0x8B,
+ 0x8A, 0x5F, 0xFC, 0x4D, 0xAA, 0xBE, 0xBD, 0xE6, 0x64, 0x9F, 0x26, 0x9A, 0x2B,
+ 0x97, 0x69, 0xA9, 0xBA, 0x0B, 0xBD, 0x48, 0xE4, 0x81, 0x6B, 0xD4, 0x4B, 0x78,
+ 0xE6, 0xAF, 0x95, 0x66, 0xC1, 0x23, 0xDA, 0x23, 0x45, 0x36, 0x6E, 0x25, 0xF3,
+ 0xC7, 0xC0, 0x61, 0xFC, 0xEC, 0x66, 0x9D, 0x31, 0xD4, 0xD6, 0xB6, 0x36, 0xE3,
+ 0x7F, 0x81, 0x87, 0x02, 0x03, 0x01, 0x00, 0x01
+};
+#endif
+
+const char kPolicyVerificationKeyHash[] = "1:356l7w";
+
+std::string GetPolicyVerificationKey() {
+#if defined(OS_CHROMEOS)
+ // TODO(atwilson): Enable this for ChromeOS. We don't verify the extra
+ // verification signatures on ChromeOS to simplify testing, and because
+ // ChromeOS doesn't need the extra verification because it already stores
+ // the policy blob securely.
+ return std::string();
+#else
+ // Allow disabling key verification until we update the test servers with
+ // verification signatures for all QA domains (http://crbug.com/328038).
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kSkipPolicyKeyVerification)) {
+ return std::string();
+ } else {
+ return std::string(reinterpret_cast<const char*>(kPolicyVerificationKey),
+ sizeof(kPolicyVerificationKey));
+ }
+#endif
+}
+
const char* GetChromeUserPolicyType() {
#if defined(OS_ANDROID) || defined(OS_IOS)
CommandLine* command_line = CommandLine::ForCurrentProcess();
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h
index c0a7e1b..c841d76 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -51,6 +51,11 @@ enum PolicyFetchStatus {
} // namespace dm_protocol
+// Information about the verification key used to verify that policy signing
+// keys are valid.
+POLICY_EXPORT std::string GetPolicyVerificationKey();
+POLICY_EXPORT extern const char kPolicyVerificationKeyHash[];
+
// Describes the affiliation of a user w.r.t. the device owner.
enum UserAffiliation {
// User is on the same domain the device was registered with.
diff --git a/components/policy/core/common/cloud/cloud_policy_validator.cc b/components/policy/core/common/cloud/cloud_policy_validator.cc
index 5bbc6bc..f85561d 100644
--- a/components/policy/core/common/cloud/cloud_policy_validator.cc
+++ b/components/policy/core/common/cloud/cloud_policy_validator.cc
@@ -6,6 +6,7 @@
#include "base/bind_helpers.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
@@ -28,6 +29,23 @@ const uint8 kSignatureAlgorithm[] = {
0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00
};
+const char kMetricPolicyKeyVerification[] = "Enterprise.PolicyKeyVerification";
+
+enum MetricPolicyKeyVerification {
+ // UMA metric recorded when the client has no verification key.
+ METRIC_POLICY_KEY_VERIFICATION_KEY_MISSING,
+ // Recorded when the policy being verified has no key signature (e.g. policy
+ // fetched before the server supported the verification key).
+ METRIC_POLICY_KEY_VERIFICATION_SIGNATURE_MISSING,
+ // Recorded when the key signature did not match the expected value (in
+ // theory, this should only happen after key rotation or if the policy cached
+ // on disk has been modified).
+ METRIC_POLICY_KEY_VERIFICATION_FAILED,
+ // Recorded when key verification succeeded.
+ METRIC_POLICY_KEY_VERIFICATION_SUCCEEDED,
+ METRIC_POLICY_KEY_VERIFICATION_SIZE // Must be the last.
+};
+
} // namespace
CloudPolicyValidatorBase::~CloudPolicyValidatorBase() {}
@@ -83,16 +101,22 @@ void CloudPolicyValidatorBase::ValidatePayload() {
validation_flags_ |= VALIDATE_PAYLOAD;
}
-void CloudPolicyValidatorBase::ValidateSignature(const std::vector<uint8>& key,
- bool allow_key_rotation) {
+void CloudPolicyValidatorBase::ValidateSignature(
+ const std::string& key,
+ const std::string& verification_key,
+ const std::string& key_signature,
+ bool allow_key_rotation) {
validation_flags_ |= VALIDATE_SIGNATURE;
- key_ = std::string(reinterpret_cast<const char*>(vector_as_array(&key)),
- key.size());
+ set_verification_key(verification_key);
+ key_ = key;
+ key_signature_ = key_signature;
allow_key_rotation_ = allow_key_rotation;
}
-void CloudPolicyValidatorBase::ValidateInitialKey() {
+void CloudPolicyValidatorBase::ValidateInitialKey(
+ const std::string& verification_key) {
validation_flags_ |= VALIDATE_INITIAL_KEY;
+ set_verification_key(verification_key);
}
void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
@@ -210,6 +234,65 @@ void CloudPolicyValidatorBase::RunChecks() {
}
}
+// Verifies the |new_public_key_verification_signature| for the |new_public_key|
+// in the policy blob.
+bool CloudPolicyValidatorBase::CheckNewPublicKeyVerificationSignature() {
+ // If there's no local verification key, then just return true (no
+ // validation possible).
+ if (verification_key_.empty()) {
+ UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
+ METRIC_POLICY_KEY_VERIFICATION_KEY_MISSING,
+ METRIC_POLICY_KEY_VERIFICATION_SIZE);
+ return true;
+ }
+
+ if (!policy_->has_new_public_key_verification_signature()) {
+ // Policy does not contain a verification signature, so log an error.
+ LOG(ERROR) << "Policy is missing public_key_verification_signature";
+ UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
+ METRIC_POLICY_KEY_VERIFICATION_SIGNATURE_MISSING,
+ METRIC_POLICY_KEY_VERIFICATION_SIZE);
+ // TODO(atwilson): Return an error on failed signature verification once
+ // our test servers and unittests are returning policy with a verification
+ // signature (http://crbug.com/275291).
+ return true;
+ }
+
+ if (!CheckVerificationKeySignature(
+ policy_->new_public_key(),
+ verification_key_,
+ policy_->new_public_key_verification_signature())) {
+ LOG(ERROR) << "Signature verification failed";
+ UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
+ METRIC_POLICY_KEY_VERIFICATION_FAILED,
+ METRIC_POLICY_KEY_VERIFICATION_SIZE);
+ // TODO(atwilson): Update this code to include the domain name in the
+ // signature, and return an error once the server starts returning this.
+ return true;
+ }
+ // Signature verification succeeded - return success to the caller.
+ UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification,
+ METRIC_POLICY_KEY_VERIFICATION_SUCCEEDED,
+ METRIC_POLICY_KEY_VERIFICATION_SIZE);
+ return true;
+}
+
+bool CloudPolicyValidatorBase::CheckVerificationKeySignature(
+ const std::string& key,
+ const std::string& verification_key,
+ const std::string& signature) {
+ // TODO(atwilson): Update this routine to include the domain name in the
+ // signed data.
+ return VerifySignature(key, verification_key, signature);
+}
+
+void CloudPolicyValidatorBase::set_verification_key(
+ const std::string& verification_key) {
+ // Make sure we aren't overwriting the verification key with a different key.
+ DCHECK(verification_key_.empty() || verification_key_ == verification_key);
+ verification_key_ = verification_key;
+}
+
CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
const std::string* signature_key = &key_;
if (policy_->has_new_public_key() && allow_key_rotation_) {
@@ -217,9 +300,14 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
if (!policy_->has_new_public_key_signature() ||
!VerifySignature(policy_->new_public_key(), key_,
policy_->new_public_key_signature())) {
- LOG(ERROR) << "New public key signature verification failed";
+ LOG(ERROR) << "New public key rotation signature verification failed";
return VALIDATION_BAD_SIGNATURE;
}
+
+ if (!CheckNewPublicKeyVerificationSignature()) {
+ LOG(ERROR) << "New public key root verification failed";
+ return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
+ }
}
if (!policy_->has_policy_data_signature() ||
@@ -229,6 +317,16 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
return VALIDATION_BAD_SIGNATURE;
}
+ // If a key verification signature is available, then verify the base signing
+ // key as well.
+ if (!key_signature_.empty() &&
+ !CheckVerificationKeySignature(key_, verification_key_, key_signature_)) {
+ LOG(ERROR) << "Verification key signature verification failed";
+ // TODO(atwilson): Update to return an error once the server is properly
+ // generating a verification signature (http://crbug.com/275291).
+ // return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
+ }
+
return VALIDATION_OK;
}
@@ -241,6 +339,10 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
return VALIDATION_BAD_INITIAL_SIGNATURE;
}
+ if (!CheckNewPublicKeyVerificationSignature()) {
+ LOG(ERROR) << "Initial policy root signature validation failed";
+ return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
+ }
return VALIDATION_OK;
}
diff --git a/components/policy/core/common/cloud/cloud_policy_validator.h b/components/policy/core/common/cloud/cloud_policy_validator.h
index 3d2f2aa..541fa78 100644
--- a/components/policy/core/common/cloud/cloud_policy_validator.h
+++ b/components/policy/core/common/cloud/cloud_policy_validator.h
@@ -46,9 +46,9 @@ namespace policy {
// RunValidation() can be used to perform validation on the current thread.
class POLICY_EXPORT CloudPolicyValidatorBase {
public:
- // Validation result codes. These values are also used for UMA histograms;
- // they must stay stable, and the UMA counters must be updated if new elements
- // are appended at the end.
+ // Validation result codes. These values are also used for UMA histograms by
+ // UserCloudPolicyStoreChromeOS and must stay stable - new elements should
+ // be added at the end before VALIDATION_STATUS_SIZE.
enum Status {
// Indicates successful validation.
VALIDATION_OK,
@@ -72,6 +72,10 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
VALIDATION_BAD_USERNAME,
// Policy payload protobuf parse error.
VALIDATION_POLICY_PARSE_ERROR,
+ // Policy key signature could not be verified using the hard-coded
+ // verification key.
+ VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE,
+ VALIDATION_STATUS_SIZE // MUST BE LAST
};
enum ValidateDMTokenOption {
@@ -143,19 +147,25 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// Validates that the payload can be decoded successfully.
void ValidatePayload();
- // Verifies that the signature on the policy blob verifies against |key|. If |
+ // Verifies that the signature on the policy blob verifies against |key|. If
// |allow_key_rotation| is true and there is a key rotation present in the
// policy blob, this checks the signature on the new key against |key| and the
- // policy blob against the new key.
- void ValidateSignature(const std::vector<uint8>& key,
+ // policy blob against the new key. New key is also validated using the passed
+ // |verification_key| and the |new_public_key_verification_signature| field.
+ // If |key_signature| is non-empty, then |key| is also verified against that
+ // signature (useful when dealing with cached keys from untrusted sources).
+ void ValidateSignature(const std::string& key,
+ const std::string& verification_key,
+ const std::string& key_signature,
bool allow_key_rotation);
- // Similar to StartSignatureVerification(), this checks the signature on the
+ // Similar to ValidateSignature(), this checks the signature on the
// policy blob. However, this variant expects a new policy key set in the
// policy blob and makes sure the policy is signed using that key. This should
// be called at setup time when there is no existing policy key present to
- // check against.
- void ValidateInitialKey();
+ // check against. New key is validated using the passed |verification_key| and
+ // the new_public_key_verification_signature field.
+ void ValidateInitialKey(const std::string& verification_key);
// Convenience helper that configures timestamp and token validation based on
// the current policy blob. |policy_data| may be NULL, in which case the
@@ -195,6 +205,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
VALIDATE_PAYLOAD = 1 << 6,
VALIDATE_SIGNATURE = 1 << 7,
VALIDATE_INITIAL_KEY = 1 << 8,
+ VALIDATE_SIGNED_KEY = 1 << 9,
};
// Performs validation, called on a background thread.
@@ -210,6 +221,21 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// Invokes all the checks and reports the result.
void RunChecks();
+ // Helper routine that verifies that the new public key in the policy blob
+ // is properly signed by the |verification_key_|.
+ bool CheckNewPublicKeyVerificationSignature();
+
+ // Helper routine that performs a verification-key-based signature check,
+ // which includes the domain name associated with this policy. Returns true
+ // if the verification succeeds, or if |signature| is empty.
+ bool CheckVerificationKeySignature(const std::string& key_to_verify,
+ const std::string& server_key,
+ const std::string& signature);
+
+ // Sets the key used to verify new public keys, and ensures that callers
+ // don't try to set conflicting keys.
+ void set_verification_key(const std::string& verification_key);
+
// Helper functions implementing individual checks.
Status CheckTimestamp();
Status CheckUsername();
@@ -242,6 +268,8 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
std::string policy_type_;
std::string settings_entity_id_;
std::string key_;
+ std::string key_signature_;
+ std::string verification_key_;
bool allow_key_rotation_;
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
diff --git a/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc b/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
index c0e165e..18dc7be 100644
--- a/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_validator_unittest.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/cloud_policy_validator.h"
@@ -57,11 +58,18 @@ class CloudPolicyValidatorTest : public testing::Test {
}
scoped_ptr<UserCloudPolicyValidator> CreateValidator() {
- std::vector<uint8> public_key;
+ std::vector<uint8> public_key_bytes;
EXPECT_TRUE(
- PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(&public_key));
+ PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(
+ &public_key_bytes));
policy_.Build();
+ // Convert from bytes to string format (which is what ValidateSignature()
+ // takes).
+ std::string public_key = std::string(
+ reinterpret_cast<const char*>(vector_as_array(&public_key_bytes)),
+ public_key_bytes.size());
+
UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create(
policy_.GetCopy(), base::MessageLoopProxy::current());
validator->ValidateTimestamp(timestamp_, timestamp_,
@@ -71,9 +79,12 @@ class CloudPolicyValidatorTest : public testing::Test {
validator->ValidateDMToken(existing_dm_token_, ignore_missing_dm_token_);
validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType);
validator->ValidatePayload();
- validator->ValidateSignature(public_key, allow_key_rotation_);
+ validator->ValidateSignature(public_key,
+ GetPolicyVerificationKey(),
+ PolicyBuilder::GetTestSigningKeySignature(),
+ allow_key_rotation_);
if (allow_key_rotation_)
- validator->ValidateInitialKey();
+ validator->ValidateInitialKey(GetPolicyVerificationKey());
return make_scoped_ptr(validator);
}
diff --git a/components/policy/core/common/cloud/mock_cloud_policy_client.cc b/components/policy/core/common/cloud/mock_cloud_policy_client.cc
index bbaec1f..e36bebc 100644
--- a/components/policy/core/common/cloud/mock_cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/mock_cloud_policy_client.cc
@@ -13,6 +13,7 @@ namespace policy {
MockCloudPolicyClient::MockCloudPolicyClient()
: CloudPolicyClient(std::string(),
std::string(),
+ std::string(),
USER_AFFILIATION_NONE,
NULL,
NULL,
diff --git a/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc b/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc
index 74166ca..a65a206 100644
--- a/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc
+++ b/components/policy/core/common/cloud/mock_user_cloud_policy_store.cc
@@ -8,6 +8,8 @@ namespace policy {
MockUserCloudPolicyStore::MockUserCloudPolicyStore()
: UserCloudPolicyStore(base::FilePath(),
+ base::FilePath(),
+ std::string(),
scoped_refptr<base::SequencedTaskRunner>()) {}
MockUserCloudPolicyStore::~MockUserCloudPolicyStore() {}
diff --git a/components/policy/core/common/cloud/policy_builder.cc b/components/policy/core/common/cloud/policy_builder.cc
index 3d00067..09c09fe 100644
--- a/components/policy/core/common/cloud/policy_builder.cc
+++ b/components/policy/core/common/cloud/policy_builder.cc
@@ -144,6 +144,13 @@ void PolicyBuilder::SetDefaultNewSigningKey() {
raw_new_signing_key_.swap(key);
}
+void PolicyBuilder::SetDefaultInitialSigningKey() {
+ std::vector<uint8> key(kSigningKey,
+ kSigningKey + arraysize(kSigningKey));
+ raw_new_signing_key_.swap(key);
+ UnsetSigningKey();
+}
+
void PolicyBuilder::UnsetNewSigningKey() {
raw_new_signing_key_.clear();
}
@@ -169,6 +176,10 @@ void PolicyBuilder::Build() {
policy_.mutable_new_public_key_signature());
}
} else {
+ // No new signing key, so clear the old public key (this allows us to
+ // reuse the same PolicyBuilder to build multiple policy blobs).
+ policy_.clear_new_public_key();
+ policy_.clear_new_public_key_signature();
policy_signing_key = GetSigningKey();
}
@@ -205,6 +216,18 @@ scoped_ptr<crypto::RSAPrivateKey> PolicyBuilder::CreateTestOtherSigningKey() {
crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(raw_new_signing_key));
}
+// static
+std::string PolicyBuilder::GetTestSigningKeySignature() {
+ // TODO(atwilson): Return a real verification signature when one is available.
+ return std::string();
+}
+
+// static
+std::string PolicyBuilder::GetTestOtherSigningKeySignature() {
+ // TODO(atwilson): Return a real verification signature when one is available.
+ return std::string();
+}
+
void PolicyBuilder::SignData(const std::string& data,
crypto::RSAPrivateKey* key,
std::string* signature) {
diff --git a/components/policy/core/common/cloud/policy_builder.h b/components/policy/core/common/cloud/policy_builder.h
index 2298b3a..20114c6 100644
--- a/components/policy/core/common/cloud/policy_builder.h
+++ b/components/policy/core/common/cloud/policy_builder.h
@@ -61,6 +61,11 @@ class PolicyBuilder {
void SetDefaultSigningKey();
void UnsetSigningKey();
+ // Sets the default initial signing key - the resulting policy will be signed
+ // by the default signing key, and will have that key set as the
+ // new_public_key field, as if it were an initial key provision.
+ void SetDefaultInitialSigningKey();
+
scoped_ptr<crypto::RSAPrivateKey> GetNewSigningKey();
void SetDefaultNewSigningKey();
void UnsetNewSigningKey();
@@ -79,6 +84,11 @@ class PolicyBuilder {
static scoped_ptr<crypto::RSAPrivateKey> CreateTestSigningKey();
static scoped_ptr<crypto::RSAPrivateKey> CreateTestOtherSigningKey();
+ // Verification signatures for the two hard-coded testing keys above. These
+ // signatures are valid only for the kFakeDomain domain.
+ static std::string GetTestSigningKeySignature();
+ static std::string GetTestOtherSigningKeySignature();
+
private:
// Produces |key|'s signature over |data| and stores it in |signature|.
void SignData(const std::string& data,
diff --git a/components/policy/core/common/cloud/policy_header_service.cc b/components/policy/core/common/cloud/policy_header_service.cc
index 7953f56..3ba5b03 100644
--- a/components/policy/core/common/cloud/policy_header_service.cc
+++ b/components/policy/core/common/cloud/policy_header_service.cc
@@ -12,14 +12,19 @@
namespace {
const char kUserDMTokenKey[] = "user_dmtoken";
+const char kUserPolicyTokenKey[] = "user_policy_token";
+const char kVerificationKeyHashKey[] = "verification_key_hash";
}
namespace policy {
-PolicyHeaderService::PolicyHeaderService(const std::string& server_url,
- CloudPolicyStore* user_policy_store,
- CloudPolicyStore* device_policy_store)
+PolicyHeaderService::PolicyHeaderService(
+ const std::string& server_url,
+ const std::string& verification_key_hash,
+ CloudPolicyStore* user_policy_store,
+ CloudPolicyStore* device_policy_store)
: server_url_(server_url),
+ verification_key_hash_(verification_key_hash),
user_policy_store_(user_policy_store),
device_policy_store_(device_policy_store) {
user_policy_store_->AddObserver(this);
@@ -54,10 +59,20 @@ std::string PolicyHeaderService::CreateHeaderValue() {
// {
// user_dmtoken: <dm_token>
// user_policy_token: <policy_token>
+ // verification_key_hash: <key_hash>
// }
std::string user_dm_token = user_policy_store_->policy()->request_token();
base::DictionaryValue value;
value.SetString(kUserDMTokenKey, user_dm_token);
+ // TODO(atwilson): Enable this once policy token is available.
+ //if (user_policy_store_->policy()->has_policy_token()) {
+ // value.SetString(kUserPolicyTokenKey,
+ // user_policy_store_->policy()->policy_token());
+ //}
+ value.SetString(kUserPolicyTokenKey, "");
+ if (!verification_key_hash_.empty())
+ value.SetString(kVerificationKeyHashKey, verification_key_hash_);
+
// TODO(atwilson): add user_policy_token once the server starts sending it
// down (http://crbug.com/326799).
std::string json;
diff --git a/components/policy/core/common/cloud/policy_header_service.h b/components/policy/core/common/cloud/policy_header_service.h
index b797c2a..e55c08b 100644
--- a/components/policy/core/common/cloud/policy_header_service.h
+++ b/components/policy/core/common/cloud/policy_header_service.h
@@ -30,6 +30,7 @@ class POLICY_EXPORT PolicyHeaderService : public CloudPolicyStore::Observer {
// device policy. Both |user_policy_store| and |device_policy_store| must
// outlive this object.
PolicyHeaderService(const std::string& server_url,
+ const std::string& verification_key_hash,
CloudPolicyStore* user_policy_store,
CloudPolicyStore* device_policy_store);
virtual ~PolicyHeaderService();
@@ -56,6 +57,9 @@ class POLICY_EXPORT PolicyHeaderService : public CloudPolicyStore::Observer {
// URL of the policy server.
std::string server_url_;
+ // Identifier for the verification key this Chrome instance is using.
+ std::string verification_key_hash_;
+
// Weak pointers to User-/Device-level policy stores.
CloudPolicyStore* user_policy_store_;
CloudPolicyStore* device_policy_store_;
diff --git a/components/policy/core/common/cloud/policy_header_service_unittest.cc b/components/policy/core/common/cloud/policy_header_service_unittest.cc
index b8c4e5b..c5cf639 100644
--- a/components/policy/core/common/cloud/policy_header_service_unittest.cc
+++ b/components/policy/core/common/cloud/policy_header_service_unittest.cc
@@ -6,6 +6,7 @@
#include "base/json/json_reader.h"
#include "base/test/test_simple_task_runner.h"
#include "base/values.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/core/common/cloud/policy_header_io_helper.h"
#include "components/policy/core/common/cloud/policy_header_service.h"
@@ -37,8 +38,10 @@ class PolicyHeaderServiceTest : public testing::Test {
virtual ~PolicyHeaderServiceTest() {}
virtual void SetUp() OVERRIDE {
- service_.reset(new PolicyHeaderService(
- kDMServerURL, &user_store_, &device_store_));
+ service_.reset(new PolicyHeaderService(kDMServerURL,
+ kPolicyVerificationKeyHash,
+ &user_store_,
+ &device_store_));
helper_ = service_->CreatePolicyHeaderIOHelper(task_runner_).Pass();
}
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.cc b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
index 6e47339..ed9e590 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.cc
@@ -72,6 +72,7 @@ UserCloudPolicyManager::CreateCloudPolicyClient(
new CloudPolicyClient(
std::string(),
std::string(),
+ kPolicyVerificationKeyHash,
USER_AFFILIATION_NONE,
NULL,
device_management_service,
diff --git a/components/policy/core/common/cloud/user_cloud_policy_manager.h b/components/policy/core/common/cloud/user_cloud_policy_manager.h
index 5c4068e..7d5f3f5 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_manager.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_manager.h
@@ -73,7 +73,8 @@ class POLICY_EXPORT UserCloudPolicyManager : public CloudPolicyManager {
// Creates a CloudPolicyClient for this client. Used in situations where
// callers want to create a DMToken without actually initializing the
- // profile's policy infrastructure.
+ // profile's policy infrastructure (for example, during signin when we
+ // want to check if the user's domain requires policy).
static scoped_ptr<CloudPolicyClient> CreateCloudPolicyClient(
DeviceManagementService* device_management_service,
scoped_refptr<net::URLRequestContextGetter> request_context);
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.cc b/components/policy/core/common/cloud/user_cloud_policy_store.cc
index c800328..7b4d735 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.cc
@@ -7,9 +7,11 @@
#include "base/bind.h"
#include "base/file_util.h"
#include "base/location.h"
+#include "base/metrics/histogram.h"
#include "base/task_runner_util.h"
#include "policy/proto/cloud_policy.pb.h"
#include "policy/proto/device_management_backend.pb.h"
+#include "policy/proto/policy_signing_key.pb.h"
namespace em = enterprise_management;
@@ -32,6 +34,7 @@ enum PolicyLoadStatus {
struct PolicyLoadResult {
PolicyLoadStatus status;
em::PolicyFetchResponse policy;
+ em::PolicySigningKey key;
};
namespace {
@@ -42,69 +45,130 @@ const base::FilePath::CharType kPolicyDir[] = FILE_PATH_LITERAL("Policy");
const base::FilePath::CharType kPolicyCacheFile[] =
FILE_PATH_LITERAL("User Policy");
+// File in the above directory for storing policy signing key data.
+const base::FilePath::CharType kKeyCacheFile[] =
+ FILE_PATH_LITERAL("Signing Key");
+
+const char kMetricPolicyHasVerifiedCachedKey[] =
+ "Enterprise.PolicyHasVerifiedCachedKey";
+
// Loads policy from the backing file. Returns a PolicyLoadResult with the
// results of the fetch.
-policy::PolicyLoadResult LoadPolicyFromDisk(const base::FilePath& path) {
+policy::PolicyLoadResult LoadPolicyFromDisk(
+ const base::FilePath& policy_path,
+ const base::FilePath& key_path) {
policy::PolicyLoadResult result;
- // If the backing file does not exist, just return.
- if (!base::PathExists(path)) {
+ // If the backing file does not exist, just return. We don't verify the key
+ // path here, because the key is optional (the validation code will fail if
+ // the key does not exist but the loaded policy is unsigned).
+ if (!base::PathExists(policy_path)) {
result.status = policy::LOAD_RESULT_NO_POLICY_FILE;
return result;
}
std::string data;
- if (!base::ReadFileToString(path, &data) ||
- !result.policy.ParseFromArray(data.c_str(), data.size())) {
- LOG(WARNING) << "Failed to read or parse policy data from " << path.value();
+ // TODO(atwilson): Enforce a policy/key maxsize when ReadFileToString() can
+ // accept a max_size (http://crbug.com/339417).
+ if (!base::ReadFileToString(policy_path, &data) ||
+ !result.policy.ParseFromString(data)) {
+ LOG(WARNING) << "Failed to read or parse policy data from "
+ << policy_path.value();
result.status = policy::LOAD_RESULT_LOAD_ERROR;
return result;
}
+ if (!base::ReadFileToString(key_path, &data) ||
+ !result.key.ParseFromString(data)) {
+ // Log an error on missing key data, but do not trigger a load failure
+ // for now since there are still old unsigned cached policy blobs in the
+ // wild with no associated key (see kMetricPolicyHasVerifiedCachedKey UMA
+ // stat below).
+ LOG(ERROR) << "Failed to read or parse key data from " << key_path.value();
+ result.key.clear_signing_key();
+ }
+
+ // Track the occurrence of valid cached keys - when this ratio gets high
+ // enough, we can update the code to reject unsigned policy or unverified
+ // keys.
+ UMA_HISTOGRAM_BOOLEAN(kMetricPolicyHasVerifiedCachedKey,
+ result.key.has_signing_key());
+
result.status = policy::LOAD_RESULT_SUCCESS;
return result;
}
+bool WriteStringToFile(const base::FilePath path, const std::string& data) {
+ if (!base::CreateDirectory(path.DirName())) {
+ DLOG(WARNING) << "Failed to create directory " << path.DirName().value();
+ return false;
+ }
+
+ int size = data.size();
+ if (file_util::WriteFile(path, data.c_str(), size) != size) {
+ DLOG(WARNING) << "Failed to write " << path.value();
+ return false;
+ }
+
+ return true;
+}
+
// Stores policy to the backing file (must be called via a task on
// the background thread).
void StorePolicyToDiskOnBackgroundThread(
- const base::FilePath& path,
+ const base::FilePath& policy_path,
+ const base::FilePath& key_path,
const em::PolicyFetchResponse& policy) {
- DVLOG(1) << "Storing policy to " << path.value();
+ DVLOG(1) << "Storing policy to " << policy_path.value();
std::string data;
if (!policy.SerializeToString(&data)) {
DLOG(WARNING) << "Failed to serialize policy data";
return;
}
- if (!base::CreateDirectory(path.DirName())) {
- DLOG(WARNING) << "Failed to create directory " << path.DirName().value();
+ if (!WriteStringToFile(policy_path, data))
return;
- }
- int size = data.size();
- if (file_util::WriteFile(path, data.c_str(), size) != size) {
- DLOG(WARNING) << "Failed to write " << path.value();
+ if (policy.has_new_public_key()) {
+ // Write the new public key and its verification signature to a file.
+ em::PolicySigningKey key_info;
+ key_info.set_signing_key(policy.new_public_key());
+ key_info.set_signing_key_signature(
+ policy.new_public_key_verification_signature());
+ std::string key_data;
+ if (!key_info.SerializeToString(&key_data)) {
+ DLOG(WARNING) << "Failed to serialize policy signing key";
+ return;
+ }
+
+ WriteStringToFile(key_path, key_data);
}
}
} // namespace
UserCloudPolicyStore::UserCloudPolicyStore(
- const base::FilePath& path,
+ const base::FilePath& policy_path,
+ const base::FilePath& key_path,
+ const std::string& verification_key,
scoped_refptr<base::SequencedTaskRunner> background_task_runner)
: UserCloudPolicyStoreBase(background_task_runner),
weak_factory_(this),
- backing_file_path_(path) {}
+ policy_path_(policy_path),
+ key_path_(key_path),
+ verification_key_(verification_key) {}
UserCloudPolicyStore::~UserCloudPolicyStore() {}
// static
scoped_ptr<UserCloudPolicyStore> UserCloudPolicyStore::Create(
const base::FilePath& profile_path,
+ const std::string& verification_key,
scoped_refptr<base::SequencedTaskRunner> background_task_runner) {
- base::FilePath path =
+ base::FilePath policy_path =
profile_path.Append(kPolicyDir).Append(kPolicyCacheFile);
- return make_scoped_ptr(
- new UserCloudPolicyStore(path, background_task_runner));
+ base::FilePath key_path =
+ profile_path.Append(kPolicyDir).Append(kKeyCacheFile);
+ return make_scoped_ptr(new UserCloudPolicyStore(
+ policy_path, key_path, verification_key, background_task_runner));
}
void UserCloudPolicyStore::SetSigninUsername(const std::string& username) {
@@ -116,7 +180,7 @@ void UserCloudPolicyStore::LoadImmediately() {
// Cancel any pending Load/Store/Validate operations.
weak_factory_.InvalidateWeakPtrs();
// Load the policy from disk...
- PolicyLoadResult result = LoadPolicyFromDisk(backing_file_path_);
+ PolicyLoadResult result = LoadPolicyFromDisk(policy_path_, key_path_);
// ...and install it, reporting success/failure to any observers.
PolicyLoaded(false, result);
}
@@ -124,10 +188,13 @@ void UserCloudPolicyStore::LoadImmediately() {
void UserCloudPolicyStore::Clear() {
background_task_runner()->PostTask(
FROM_HERE,
- base::Bind(
- base::IgnoreResult(&base::DeleteFile), backing_file_path_, false));
+ base::Bind(base::IgnoreResult(&base::DeleteFile), policy_path_, false));
+ background_task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&base::DeleteFile), key_path_, false));
policy_.reset();
policy_map_.Clear();
+ policy_key_.clear();
NotifyStoreLoaded();
}
@@ -141,7 +208,7 @@ void UserCloudPolicyStore::Load() {
base::PostTaskAndReplyWithResult(
background_task_runner(),
FROM_HERE,
- base::Bind(&LoadPolicyFromDisk, backing_file_path_),
+ base::Bind(&LoadPolicyFromDisk, policy_path_, key_path_),
base::Bind(&UserCloudPolicyStore::PolicyLoaded,
weak_factory_.GetWeakPtr(), true));
}
@@ -163,11 +230,16 @@ void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background,
// Found policy on disk - need to validate it before it can be used.
scoped_ptr<em::PolicyFetchResponse> cloud_policy(
new em::PolicyFetchResponse(result.policy));
+ scoped_ptr<em::PolicySigningKey> key(
+ new em::PolicySigningKey(result.key));
Validate(cloud_policy.Pass(),
+ key.Pass(),
validate_in_background,
base::Bind(
&UserCloudPolicyStore::InstallLoadedPolicyAfterValidation,
- weak_factory_.GetWeakPtr()));
+ weak_factory_.GetWeakPtr(),
+ result.key.has_signing_key() ?
+ result.key.signing_key() : std::string()));
break;
}
default:
@@ -176,6 +248,7 @@ void UserCloudPolicyStore::PolicyLoaded(bool validate_in_background,
}
void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation(
+ const std::string& signing_key,
UserCloudPolicyValidator* validator) {
validation_status_ = validator->status();
if (!validator->success()) {
@@ -190,6 +263,8 @@ void UserCloudPolicyStore::InstallLoadedPolicyAfterValidation(
DVLOG(1) << "Device ID: " << validator->policy_data()->device_id();
InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
+ // Policy validation succeeded, so we know the signing key is good.
+ policy_key_ = signing_key;
status_ = STATUS_OK;
NotifyStoreLoaded();
}
@@ -201,6 +276,7 @@ void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
scoped_ptr<em::PolicyFetchResponse> policy_copy(
new em::PolicyFetchResponse(policy));
Validate(policy_copy.Pass(),
+ scoped_ptr<em::PolicySigningKey>(),
true,
base::Bind(&UserCloudPolicyStore::StorePolicyAfterValidation,
weak_factory_.GetWeakPtr()));
@@ -208,16 +284,73 @@ void UserCloudPolicyStore::Store(const em::PolicyFetchResponse& policy) {
void UserCloudPolicyStore::Validate(
scoped_ptr<em::PolicyFetchResponse> policy,
+ scoped_ptr<em::PolicySigningKey> cached_key,
bool validate_in_background,
const UserCloudPolicyValidator::CompletionCallback& callback) {
+
+ const bool signed_policy = policy->has_policy_data_signature();
+
// Configure the validator.
scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
policy.Pass(),
CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
// Validate the username if the user is signed in.
- if (!signin_username_.empty())
+ if (!signin_username_.empty()) {
+ DVLOG(1) << "Validating username: " << signin_username_;
validator->ValidateUsername(signin_username_);
+ }
+
+ // There are 4 cases:
+ //
+ // 1) Validation after loading from cache with no cached key.
+ // Action: Don't validate signature (migration from previously cached
+ // unsigned blob).
+ //
+ // 2) Validation after loading from cache with a cached key
+ // Action: Validate signature on policy blob but don't allow key rotation.
+ //
+ // 3) Validation after loading new policy from the server with no cached key
+ // Action: Validate as initial key provisioning (case where we are migrating
+ // from unsigned policy)
+ //
+ // 4) Validation after loading new policy from the server with a cached key
+ // Action: Validate as normal, and allow key rotation.
+ if (cached_key) {
+ // Loading from cache should not change the cached keys.
+ DCHECK(policy_key_.empty() || policy_key_ == cached_key->signing_key());
+ if (!signed_policy || !cached_key->has_signing_key()) {
+ // Case #1 - loading from cache with no signing key.
+ // TODO(atwilson): Reject policy with no cached key once
+ // kMetricPolicyHasVerifiedCachedKey rises to a high enough level.
+ DLOG(WARNING) << "Allowing unsigned cached blob for migration";
+ } else {
+ // Case #2 - loading from cache with a cached key - just do normal
+ // signature validation using this key. We're loading from cache so don't
+ // allow key rotation.
+ const bool no_rotation = false;
+ validator->ValidateSignature(cached_key->signing_key(),
+ verification_key_,
+ cached_key->signing_key_signature(),
+ no_rotation);
+ }
+ } else {
+ // No passed cached_key - this is not validating the initial policy load
+ // from cache, but rather an update from the server.
+ if (policy_key_.empty()) {
+ // Case #3 - no valid existing policy key, so this new policy fetch should
+ // include an initial key provision.
+ validator->ValidateInitialKey(verification_key_);
+ } else {
+ // Case #4 - verify new policy with existing key. We always allow key
+ // rotation - the verification key will prevent invalid policy from being
+ // injected. |policy_key_| is already known to be valid, so no
+ // verification signature is passed in.
+ const bool allow_rotation = true;
+ validator->ValidateSignature(
+ policy_key_, verification_key_, std::string(), allow_rotation);
+ }
+ }
if (validate_in_background) {
// Start validation in the background. The Validator will free itself once
@@ -245,8 +378,12 @@ void UserCloudPolicyStore::StorePolicyAfterValidation(
background_task_runner()->PostTask(
FROM_HERE,
base::Bind(&StorePolicyToDiskOnBackgroundThread,
- backing_file_path_, *validator->policy()));
+ policy_path_, key_path_, *validator->policy()));
InstallPolicy(validator->policy_data().Pass(), validator->payload().Pass());
+
+ // If the key was rotated, update our local cache of the key.
+ if (validator->policy()->has_new_public_key())
+ policy_key_ = validator->policy()->new_public_key();
status_ = STATUS_OK;
NotifyStoreLoaded();
}
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store.h b/components/policy/core/common/cloud/user_cloud_policy_store.h
index 4dc7e24..e7ef91e 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store.h
+++ b/components/policy/core/common/cloud/user_cloud_policy_store.h
@@ -13,6 +13,7 @@
#include "base/memory/weak_ptr.h"
#include "components/policy/core/common/cloud/user_cloud_policy_store_base.h"
#include "components/policy/policy_export.h"
+#include "policy/proto/policy_signing_key.pb.h"
namespace base {
class SequencedTaskRunner;
@@ -29,6 +30,8 @@ class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
// it) user.
UserCloudPolicyStore(
const base::FilePath& policy_file,
+ const base::FilePath& key_file,
+ const std::string& verification_key,
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
virtual ~UserCloudPolicyStore();
@@ -36,6 +39,7 @@ class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
// |profile_path|.
static scoped_ptr<UserCloudPolicyStore> Create(
const base::FilePath& profile_path,
+ const std::string& verification_key,
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
// Sets the username from signin for validation of the policy.
@@ -53,6 +57,10 @@ class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
virtual void Store(
const enterprise_management::PolicyFetchResponse& policy) OVERRIDE;
+ // The key used to sign the current policy (empty if there either is no
+ // loaded policy yet, or if the policy is unsigned).
+ const std::string& policy_key() { return policy_key_; }
+
protected:
std::string signin_username_;
@@ -69,12 +77,14 @@ class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
// thread).
void Validate(
scoped_ptr<enterprise_management::PolicyFetchResponse> policy,
+ scoped_ptr<enterprise_management::PolicySigningKey> key,
bool validate_in_background,
const UserCloudPolicyValidator::CompletionCallback& callback);
// Callback invoked to install a just-loaded policy after validation has
// finished.
- void InstallLoadedPolicyAfterValidation(UserCloudPolicyValidator* validator);
+ void InstallLoadedPolicyAfterValidation(const std::string& signing_key,
+ UserCloudPolicyValidator* validator);
// Callback invoked to store the policy after validation has finished.
void StorePolicyAfterValidation(UserCloudPolicyValidator* validator);
@@ -82,8 +92,17 @@ class POLICY_EXPORT UserCloudPolicyStore : public UserCloudPolicyStoreBase {
// WeakPtrFactory used to create callbacks for validating and storing policy.
base::WeakPtrFactory<UserCloudPolicyStore> weak_factory_;
+ // The key used to verify signatures of cached policy.
+ std::string policy_key_;
+
// Path to file where we store persisted policy.
- base::FilePath backing_file_path_;
+ base::FilePath policy_path_;
+
+ // Path to file where we store the signing key for the policy blob.
+ base::FilePath key_path_;
+
+ // The hard-coded key used to verify new signing keys.
+ const std::string verification_key_;
DISALLOW_COPY_AND_ASSIGN(UserCloudPolicyStore);
};
diff --git a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
index c7378c7..0162e0a 100644
--- a/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
+++ b/components/policy/core/common/cloud/user_cloud_policy_store_unittest.cc
@@ -9,6 +9,7 @@
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
+#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/policy/core/common/cloud/mock_cloud_external_data_manager.h"
#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
#include "components/policy/core/common/cloud/policy_builder.h"
@@ -32,6 +33,8 @@ void RunUntilIdle() {
run_loop.RunUntilIdle();
}
+} // namespace
+
class UserCloudPolicyStoreTest : public testing::Test {
public:
UserCloudPolicyStoreTest() {}
@@ -39,15 +42,21 @@ class UserCloudPolicyStoreTest : public testing::Test {
virtual void SetUp() OVERRIDE {
ASSERT_TRUE(tmp_dir_.CreateUniqueTempDir());
store_.reset(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
external_data_manager_.reset(new MockCloudExternalDataManager);
external_data_manager_->SetPolicyStore(store_.get());
store_->SetSigninUsername(PolicyBuilder::kFakeUsername);
store_->AddObserver(&observer_);
- policy_.payload().mutable_passwordmanagerenabled()->set_value(true);
- policy_.payload().mutable_urlblacklist()->mutable_value()->add_entries(
- "chromium.org");
+ // Install an initial public key, so that by default the validation of
+ // the stored/loaded policy blob succeeds (it looks like a new key
+ // provision).
+ policy_.SetDefaultInitialSigningKey();
+
+ InitPolicyPayload(&policy_.payload());
policy_.Build();
}
@@ -59,10 +68,20 @@ class UserCloudPolicyStoreTest : public testing::Test {
RunUntilIdle();
}
+ void InitPolicyPayload(enterprise_management::CloudPolicySettings* payload) {
+ payload->mutable_passwordmanagerenabled()->set_value(true);
+ payload->mutable_urlblacklist()->mutable_value()->add_entries(
+ "chromium.org");
+ }
+
base::FilePath policy_file() {
return tmp_dir_.path().AppendASCII("policy");
}
+ base::FilePath key_file() {
+ return tmp_dir_.path().AppendASCII("policy_key");
+ }
+
// Verifies that store_->policy_map() has the appropriate entries.
void VerifyPolicyMap(CloudPolicyStore* store) {
EXPECT_EQ(2U, store->policy_map().size());
@@ -81,6 +100,18 @@ class UserCloudPolicyStoreTest : public testing::Test {
Eq(error)))));
}
+ void StorePolicyAndEnsureLoaded(
+ const enterprise_management::PolicyFetchResponse& policy) {
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->Store(policy);
+ RunUntilIdle();
+ Mock::VerifyAndClearExpectations(external_data_manager_.get());
+ Mock::VerifyAndClearExpectations(&observer_);
+ ASSERT_TRUE(store_->policy());
+ }
+
UserPolicyBuilder policy_;
MockCloudPolicyStoreObserver observer_;
scoped_ptr<UserCloudPolicyStore> store_;
@@ -162,20 +193,54 @@ TEST_F(UserCloudPolicyStoreTest, LoadImmediatelyWithInvalidFile) {
EXPECT_TRUE(store_->policy_map().empty());
}
+// Load file from cache with no key data, then migrate to have a key.
+TEST_F(UserCloudPolicyStoreTest, Migration) {
+ UserPolicyBuilder unsigned_builder;
+ unsigned_builder.UnsetSigningKey();
+ InitPolicyPayload(&unsigned_builder.payload());
+ unsigned_builder.Build();
+ // Policy should be unsigned.
+ EXPECT_FALSE(unsigned_builder.policy().has_policy_data_signature());
+
+ // Write policy to disk.
+ std::string data;
+ ASSERT_TRUE(unsigned_builder.policy().SerializeToString(&data));
+ ASSERT_TRUE(base::CreateDirectory(policy_file().DirName()));
+ int size = data.size();
+ ASSERT_EQ(size, file_util::WriteFile(policy_file(), data.c_str(), size));
+
+ // Now make sure the data can get loaded.
+ Sequence s;
+ EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
+ EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+ store_->LoadImmediately(); // Should load without running the message loop.
+ Mock::VerifyAndClearExpectations(external_data_manager_.get());
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ ASSERT_TRUE(store_->policy());
+ EXPECT_EQ(unsigned_builder.policy_data().SerializeAsString(),
+ store_->policy()->SerializeAsString());
+ VerifyPolicyMap(store_.get());
+ EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
+ EXPECT_TRUE(store_->policy_key().empty());
+ EXPECT_FALSE(base::PathExists(key_file()));
+
+ // Now mimic a new policy coming down - this should result in a new key
+ // being installed.
+ StorePolicyAndEnsureLoaded(policy_.policy());
+ EXPECT_EQ(policy_.policy().new_public_key(), store_->policy_key());
+ EXPECT_TRUE(base::PathExists(key_file()));
+}
+
TEST_F(UserCloudPolicyStoreTest, Store) {
EXPECT_FALSE(store_->policy());
EXPECT_TRUE(store_->policy_map().empty());
// Store a simple policy and make sure it ends up as the currently active
// policy.
- Sequence s;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
- store_->Store(policy_.policy());
- RunUntilIdle();
+ StorePolicyAndEnsureLoaded(policy_.policy());
// Policy should be decoded and stored.
- ASSERT_TRUE(store_->policy());
EXPECT_EQ(policy_.policy_data().SerializeAsString(),
store_->policy()->SerializeAsString());
VerifyPolicyMap(store_.get());
@@ -188,18 +253,9 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenClear) {
// Store a simple policy and make sure the file exists.
// policy.
- Sequence s1;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s1);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s1);
- store_->Store(policy_.policy());
- RunUntilIdle();
-
- EXPECT_TRUE(store_->policy());
+ StorePolicyAndEnsureLoaded(policy_.policy());
EXPECT_FALSE(store_->policy_map().empty());
- Mock::VerifyAndClearExpectations(external_data_manager_.get());
- Mock::VerifyAndClearExpectations(&observer_);
-
// Policy file should exist.
ASSERT_TRUE(base::PathExists(policy_file()));
@@ -218,6 +274,47 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenClear) {
EXPECT_EQ(CloudPolicyStore::STATUS_OK, store_->status());
}
+TEST_F(UserCloudPolicyStoreTest, StoreRotatedKey) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Store a simple policy and make sure it ends up as the currently active
+ // policy.
+ StorePolicyAndEnsureLoaded(policy_.policy());
+ EXPECT_FALSE(policy_.policy().has_new_public_key_signature());
+ std::string original_policy_key = policy_.policy().new_public_key();
+ EXPECT_EQ(original_policy_key, store_->policy_key());
+
+ // Now do key rotation.
+ policy_.SetDefaultSigningKey();
+ policy_.SetDefaultNewSigningKey();
+ policy_.Build();
+ EXPECT_TRUE(policy_.policy().has_new_public_key_signature());
+ EXPECT_NE(original_policy_key, policy_.policy().new_public_key());
+ StorePolicyAndEnsureLoaded(policy_.policy());
+ EXPECT_EQ(policy_.policy().new_public_key(), store_->policy_key());
+}
+
+TEST_F(UserCloudPolicyStoreTest, ProvisionKeyTwice) {
+ EXPECT_FALSE(store_->policy());
+ EXPECT_TRUE(store_->policy_map().empty());
+
+ // Store a simple policy and make sure it ends up as the currently active
+ // policy.
+ StorePolicyAndEnsureLoaded(policy_.policy());
+
+ // Now try sending down policy signed with a different key (i.e. do key
+ // rotation with a key not signed with the original signing key).
+ policy_.UnsetSigningKey();
+ policy_.SetDefaultNewSigningKey();
+ policy_.Build();
+ EXPECT_FALSE(policy_.policy().has_new_public_key_signature());
+
+ ExpectError(store_.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
+ store_->Store(policy_.policy());
+ RunUntilIdle();
+}
+
TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
EXPECT_FALSE(store_->policy());
EXPECT_TRUE(store_->policy_map().empty());
@@ -225,28 +322,20 @@ TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
// Store a simple policy then store a second policy before the first one
// finishes validating, and make sure the second policy ends up as the active
// policy.
- Sequence s1;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s1);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s1);
-
UserPolicyBuilder first_policy;
+ first_policy.SetDefaultInitialSigningKey();
first_policy.payload().mutable_passwordmanagerenabled()->set_value(false);
first_policy.Build();
- store_->Store(first_policy.policy());
- RunUntilIdle();
-
- Mock::VerifyAndClearExpectations(external_data_manager_.get());
- Mock::VerifyAndClearExpectations(&observer_);
-
- Sequence s2;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s2);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s2);
+ StorePolicyAndEnsureLoaded(first_policy.policy());
- store_->Store(policy_.policy());
- RunUntilIdle();
+ // Rebuild policy with the same signing key as |first_policy| (no rotation).
+ policy_.UnsetNewSigningKey();
+ policy_.SetDefaultSigningKey();
+ policy_.Build();
+ ASSERT_FALSE(policy_.policy().has_new_public_key());
+ StorePolicyAndEnsureLoaded(policy_.policy());
// Policy should be decoded and stored.
- ASSERT_TRUE(store_->policy());
EXPECT_EQ(policy_.policy_data().SerializeAsString(),
store_->policy()->SerializeAsString());
VerifyPolicyMap(store_.get());
@@ -256,15 +345,15 @@ TEST_F(UserCloudPolicyStoreTest, StoreTwoTimes) {
TEST_F(UserCloudPolicyStoreTest, StoreThenLoad) {
// Store a simple policy and make sure it can be read back in.
// policy.
- Sequence s;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
- store_->Store(policy_.policy());
- RunUntilIdle();
+ StorePolicyAndEnsureLoaded(policy_.policy());
+ EXPECT_FALSE(store_->policy_key().empty());
// Now, make sure the policy can be read back in from a second store.
scoped_ptr<UserCloudPolicyStore> store2(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
store2->AddObserver(&observer_);
EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
@@ -277,20 +366,21 @@ TEST_F(UserCloudPolicyStoreTest, StoreThenLoad) {
VerifyPolicyMap(store2.get());
EXPECT_EQ(CloudPolicyStore::STATUS_OK, store2->status());
store2->RemoveObserver(&observer_);
+ // Make sure that we properly resurrected the keys.
+ EXPECT_EQ(store2->policy_key(), store_->policy_key());
}
TEST_F(UserCloudPolicyStoreTest, StoreThenLoadImmediately) {
// Store a simple policy and make sure it can be read back in.
// policy.
- Sequence s;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
- store_->Store(policy_.policy());
- RunUntilIdle();
+ StorePolicyAndEnsureLoaded(policy_.policy());
// Now, make sure the policy can be read back in from a second store.
scoped_ptr<UserCloudPolicyStore> store2(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
store2->SetSigninUsername(PolicyBuilder::kFakeUsername);
store2->AddObserver(&observer_);
EXPECT_CALL(observer_, OnStoreLoaded(store2.get()));
@@ -316,18 +406,28 @@ TEST_F(UserCloudPolicyStoreTest, StoreValidationError) {
ASSERT_FALSE(store_->policy());
}
-TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
- // Force a validation error by changing the username after policy is stored.
- Sequence s;
- EXPECT_CALL(*external_data_manager_, OnPolicyStoreLoaded()).InSequence(s);
- EXPECT_CALL(observer_, OnStoreLoaded(store_.get())).InSequence(s);
+TEST_F(UserCloudPolicyStoreTest, StoreUnsigned) {
+ // Create unsigned policy, try to store it, should get a validation error.
+ policy_.policy().mutable_policy_data_signature()->clear();
+
+ // Store policy.
+ ExpectError(store_.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
store_->Store(policy_.policy());
RunUntilIdle();
+ ASSERT_FALSE(store_->policy());
+}
+
+TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
+ // Force a validation error by changing the username after policy is stored.
+ StorePolicyAndEnsureLoaded(policy_.policy());
// Sign out, and sign back in as a different user, and try to load the profile
// data (should fail due to mismatched username).
scoped_ptr<UserCloudPolicyStore> store2(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
store2->SetSigninUsername("foobar@foobar.com");
store2->AddObserver(&observer_);
ExpectError(store2.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
@@ -340,7 +440,10 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
// Sign out - we should be able to load the policy (don't check usernames
// when signed out).
scoped_ptr<UserCloudPolicyStore> store3(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
store3->AddObserver(&observer_);
EXPECT_CALL(observer_, OnStoreLoaded(store3.get()));
store3->Load();
@@ -351,7 +454,10 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
// Now start a signin as a different user - this should fail validation.
scoped_ptr<UserCloudPolicyStore> store4(
- new UserCloudPolicyStore(policy_file(), loop_.message_loop_proxy()));
+ new UserCloudPolicyStore(policy_file(),
+ key_file(),
+ GetPolicyVerificationKey(),
+ loop_.message_loop_proxy()));
store4->SetSigninUsername("foobar@foobar.com");
store4->AddObserver(&observer_);
ExpectError(store4.get(), CloudPolicyStore::STATUS_VALIDATION_ERROR);
@@ -362,6 +468,8 @@ TEST_F(UserCloudPolicyStoreTest, LoadValidationError) {
store4->RemoveObserver(&observer_);
}
-} // namespace
+// TODO(atwilson): Add a test to detect tampered policy
+// (new_public_key_verification_signature() doesn't match the right key -
+// http://crbug.com/275291).
} // namespace policy
diff --git a/components/policy/core/common/policy_switches.cc b/components/policy/core/common/policy_switches.cc
index 7808085..3690ee5 100644
--- a/components/policy/core/common/policy_switches.cc
+++ b/components/policy/core/common/policy_switches.cc
@@ -24,6 +24,9 @@ const char kDisableCloudPolicyPush[] = "disable-cloud-policy-push";
// Enables fetching and storing cloud policy for components.
const char kEnableComponentCloudPolicy[] = "enable-component-cloud-policy";
+// Disables the verification of policy signing keys.
+const char kSkipPolicyKeyVerification[] = "skip-policy-key-verification";
+
#if defined(OS_ANDROID) || defined(OS_IOS)
// Registers for cloud policy using the BROWSER client type instead of the
// ANDROID_BROWSER or IOS_BROWSER types.
diff --git a/components/policy/core/common/policy_switches.h b/components/policy/core/common/policy_switches.h
index a0728c1..e14dea0 100644
--- a/components/policy/core/common/policy_switches.h
+++ b/components/policy/core/common/policy_switches.h
@@ -16,6 +16,7 @@ POLICY_EXPORT extern const char kCloudPolicyInvalidationDelay[];
POLICY_EXPORT extern const char kDeviceManagementUrl[];
POLICY_EXPORT extern const char kDisableCloudPolicyPush[];
POLICY_EXPORT extern const char kEnableComponentCloudPolicy[];
+POLICY_EXPORT extern const char kSkipPolicyKeyVerification[];
#if defined(OS_ANDROID) || defined(OS_IOS)
POLICY_EXPORT extern const char kFakeCloudPolicyType[];
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 6754abb..51ee3a6 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -172,6 +172,13 @@ message PolicyFetchRequest {
// payload delivered with the invalidation. The server interprets this value
// and the value of invalidation_version to fetch the up-to-date policy.
optional bytes invalidation_payload = 8;
+
+ // Hash string for the chrome policy verification public key which is embedded
+ // into Chrome binary. Matching private key will be used by the server
+ // to sign per-domain policy keys during key rotation. If server does not
+ // have the key which matches this hash string, that could indicate malicious
+ // or out-of-date Chrome client.
+ optional string verification_key_hash = 9;
}
// This message is included in serialized form in PolicyFetchResponse
@@ -283,6 +290,27 @@ message PolicyFetchResponse {
// on old public key), then |new_public_key_signature| is empty.
optional bytes new_public_key = 5;
optional bytes new_public_key_signature = 6;
+
+ // If new_public_key is specified, this field contains a signature
+ // of that key, signed using a key only available to DMServer.
+ // The public key portion of this well-known key is embedded into the
+ // Chrome binary. The hash of that embedded key is passed to DMServer
+ // as verification_key_hash field in PolicyFetchRequest. DMServer will
+ // pick a private key on the server which matches the hash (matches public
+ // key on the client). If DMServer is unable to find matching key, it will
+ // return an error instead of policy data.
+ // In case hash was not specified, DMServer will leave verification signature
+ // field empty (legacy behavior).
+ // In addition to the checks between new_public_key
+ // and new_public_key_signature described above, Chrome also verifies
+ // new_public_key with the embedded public key and
+ // new_public_key_verification_signature.
+ optional bytes new_public_key_verification_signature = 7;
+
+ // Server-provided identifier of the fetched policy. This is to be used
+ // by the client when requesting Policy Posture assertion through an API
+ // call or SAML flow.
+ optional bytes policy_token = 8;
}
// Request from device to server for reading policies.
diff --git a/components/policy/proto/policy_signing_key.proto b/components/policy/proto/policy_signing_key.proto
new file mode 100644
index 0000000..c038bd3
--- /dev/null
+++ b/components/policy/proto/policy_signing_key.proto
@@ -0,0 +1,20 @@
+// Copyright 2014 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.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package enterprise_management;
+
+// Contains a signing key and its signature.
+message PolicySigningKey {
+ // The key used to verify policy blobs sent down from the server.
+ optional bytes signing_key = 1;
+
+ // The signature for this signing key (verified using a hard-coded key
+ // stored in the Chrome binary). This is essentially a certificate (key
+ // signed with another well-known key that establishes a trust root).
+ optional bytes signing_key_signature = 2;
+}
diff --git a/components/policy_strings.grdp b/components/policy_strings.grdp
index 67be5ed..90ccd37 100644
--- a/components/policy_strings.grdp
+++ b/components/policy_strings.grdp
@@ -83,6 +83,9 @@
<message name="IDS_POLICY_VALIDATION_POLICY_PARSE_ERROR" desc="Message indicating a parse error in policy.">
Error parsing policy settings
</message>
+ <message name="IDS_POLICY_VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE" desc="Message indicating a policy key had an invalid verification signature.">
+ Bad verification signature.
+ </message>
<message name="IDS_POLICY_VALIDATION_UNKNOWN_ERROR" desc="Message indicating unknown error in policy validation.">
Unknown error
</message>