Commit 4f4b60e6 authored by atwilson@chromium.org's avatar atwilson@chromium.org

Update policy signature verification to include policy domain.

CloudPolicyValidator now accpets a "domain" parameter which is used to generate
verification signatures for public keys.

Broke out CloudPolicyValidator cached-key verification code into a separate
validation function: ValidateCachedKey().

Added new hard-coded signatures for our PolicyBuilder test keys for the
example.com domain.

BUG=275291
TBR=rogerta@chromium.org

Review URL: https://codereview.chromium.org/143183007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@251292 0039d316-1c4b-4281-b951-d872f2087c98
parent d9a4c376
...@@ -48,7 +48,7 @@ void DeviceCloudPolicyStoreChromeOS::Store( ...@@ -48,7 +48,7 @@ void DeviceCloudPolicyStoreChromeOS::Store(
scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy)); scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
validator->ValidateSignature(owner_key->public_key_as_string(), validator->ValidateSignature(owner_key->public_key_as_string(),
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
std::string(), install_attributes_->GetDomain(),
true); true);
validator->ValidateAgainstCurrentPolicy( validator->ValidateAgainstCurrentPolicy(
device_settings_service_->policy_data(), device_settings_service_->policy_data(),
...@@ -77,7 +77,8 @@ void DeviceCloudPolicyStoreChromeOS::InstallInitialPolicy( ...@@ -77,7 +77,8 @@ void DeviceCloudPolicyStoreChromeOS::InstallInitialPolicy(
} }
scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy)); scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
validator->ValidateInitialKey(GetPolicyVerificationKey()); validator->ValidateInitialKey(GetPolicyVerificationKey(),
install_attributes_->GetDomain());
validator.release()->StartValidation( validator.release()->StartValidation(
base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated, base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
weak_factory_.GetWeakPtr())); weak_factory_.GetWeakPtr()));
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chromeos/dbus/power_policy_controller.h" #include "chromeos/dbus/power_policy_controller.h"
#include "chromeos/dbus/session_manager_client.h" #include "chromeos/dbus/session_manager_client.h"
#include "components/policy/core/common/cloud/device_management_service.h" #include "components/policy/core/common/cloud/device_management_service.h"
...@@ -190,9 +192,11 @@ void DeviceLocalAccountPolicyStore::Validate( ...@@ -190,9 +192,11 @@ void DeviceLocalAccountPolicyStore::Validate(
: CloudPolicyValidatorBase::TIMESTAMP_NOT_REQUIRED, : CloudPolicyValidatorBase::TIMESTAMP_NOT_REQUIRED,
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED); CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePayload(); validator->ValidatePayload();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
validator->ValidateSignature(key->public_key_as_string(), validator->ValidateSignature(key->public_key_as_string(),
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
std::string(), connector->GetEnterpriseDomain(),
false); false);
validator.release()->StartValidation(callback); validator.release()->StartValidation(callback);
} }
......
...@@ -109,13 +109,24 @@ void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) { ...@@ -109,13 +109,24 @@ void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {
validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(), validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
if (install_attributes_->IsEnterpriseDevice())
validator->ValidateDomain(install_attributes_->GetDomain()); // If this is re-enrollment, make sure that the new policy matches the
// previously-enrolled domain.
std::string domain;
if (install_attributes_->IsEnterpriseDevice()) {
domain = install_attributes_->GetDomain();
validator->ValidateDomain(domain);
}
validator->ValidateDMToken(client->dm_token(), validator->ValidateDMToken(client->dm_token(),
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED); CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType); validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
validator->ValidatePayload(); validator->ValidatePayload();
validator->ValidateInitialKey(GetPolicyVerificationKey()); // If |domain| is empty here, the policy validation code will just use the
// domain from the username field in the policy itself to do key validation.
// TODO(mnissler): Plumb the enrolling user's username into this object so
// we can validate the username on the resulting policy, and use the domain
// from that username to validate the key below (http://crbug.com/343074).
validator->ValidateInitialKey(GetPolicyVerificationKey(), domain);
validator.release()->StartValidation( validator.release()->StartValidation(
base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated, base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
......
...@@ -49,6 +49,11 @@ void SampleValidationFailure(ValidationFailure sample) { ...@@ -49,6 +49,11 @@ void SampleValidationFailure(ValidationFailure sample) {
VALIDATION_FAILURE_SIZE); VALIDATION_FAILURE_SIZE);
} }
// Extracts the domain name from the passed username.
std::string ExtractDomain(const std::string& username) {
return gaia::ExtractDomainName(gaia::CanonicalizeEmail(username));
}
} // namespace } // namespace
// Helper class for loading legacy policy caches. // Helper class for loading legacy policy caches.
...@@ -259,7 +264,7 @@ void UserCloudPolicyStoreChromeOS::LoadImmediately() { ...@@ -259,7 +264,7 @@ void UserCloudPolicyStoreChromeOS::LoadImmediately() {
validator->ValidateSignature( validator->ValidateSignature(
policy_key_, policy_key_,
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
std::string(), // No signature verification needed. ExtractDomain(sanitized_username),
allow_rotation); allow_rotation);
validator->RunValidation(); validator->RunValidation();
OnRetrievedPolicyValidated(validator.get()); OnRetrievedPolicyValidated(validator.get());
...@@ -273,12 +278,13 @@ void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore( ...@@ -273,12 +278,13 @@ void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED); CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
validator->ValidateUsername(username_); validator->ValidateUsername(username_);
if (policy_key_.empty()) { if (policy_key_.empty()) {
validator->ValidateInitialKey(GetPolicyVerificationKey()); validator->ValidateInitialKey(GetPolicyVerificationKey(),
ExtractDomain(username_));
} else { } else {
const bool allow_rotation = true; const bool allow_rotation = true;
validator->ValidateSignature(policy_key_, validator->ValidateSignature(policy_key_,
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
std::string(), ExtractDomain(username_),
allow_rotation); allow_rotation);
} }
...@@ -377,7 +383,7 @@ void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy( ...@@ -377,7 +383,7 @@ void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
const bool allow_rotation = false; const bool allow_rotation = false;
validator->ValidateSignature(policy_key_, validator->ValidateSignature(policy_key_,
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
std::string(), ExtractDomain(username_),
allow_rotation); allow_rotation);
// Start validation. The Validator will delete itself once validation is // Start validation. The Validator will delete itself once validation is
// complete. // complete.
......
...@@ -41,7 +41,8 @@ namespace { ...@@ -41,7 +41,8 @@ namespace {
const char kLegacyDeviceId[] = "legacy-device-id"; const char kLegacyDeviceId[] = "legacy-device-id";
const char kLegacyToken[] = "legacy-token"; const char kLegacyToken[] = "legacy-token";
const char kSanitizedUsername[] = "0123456789ABCDEF0123456789ABCDEF012345678"; const char kSanitizedUsername[] =
"0123456789ABCDEF0123456789ABCDEF012345678@example.com";
const char kDefaultHomepage[] = "http://chromium.org"; const char kDefaultHomepage[] = "http://chromium.org";
ACTION_P2(SendSanitizedUsername, call_status, sanitized_username) { ACTION_P2(SendSanitizedUsername, call_status, sanitized_username) {
...@@ -599,7 +600,7 @@ TEST_F(UserCloudPolicyStoreChromeOSTest, LoadImmediatelyNoUserPolicyKey) { ...@@ -599,7 +600,7 @@ TEST_F(UserCloudPolicyStoreChromeOSTest, LoadImmediatelyNoUserPolicyKey) {
.WillOnce(Return(policy_.GetBlob())); .WillOnce(Return(policy_.GetBlob()));
EXPECT_CALL(cryptohome_client_, EXPECT_CALL(cryptohome_client_,
BlockingGetSanitizedUsername(PolicyBuilder::kFakeUsername)) BlockingGetSanitizedUsername(PolicyBuilder::kFakeUsername))
.WillOnce(Return("wrong")); .WillOnce(Return("wrong@example.com"));
EXPECT_FALSE(store_->policy()); EXPECT_FALSE(store_->policy());
store_->LoadImmediately(); store_->LoadImmediately();
......
...@@ -182,8 +182,10 @@ void SessionManagerOperation::ValidateDeviceSettings( ...@@ -182,8 +182,10 @@ void SessionManagerOperation::ValidateDeviceSettings(
policy::CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED); policy::CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED);
validator->ValidatePolicyType(policy::dm_protocol::kChromeDevicePolicyType); validator->ValidatePolicyType(policy::dm_protocol::kChromeDevicePolicyType);
validator->ValidatePayload(); validator->ValidatePayload();
// We don't check the DMServer verification key below, because the signing
// key is validated when it is installed.
validator->ValidateSignature(owner_key_->public_key_as_string(), validator->ValidateSignature(owner_key_->public_key_as_string(),
policy::GetPolicyVerificationKey(), std::string(), // No key validation check.
std::string(), std::string(),
false); false);
validator->StartValidation( validator->StartValidation(
......
...@@ -271,7 +271,7 @@ TEST_F(SessionManagerOperationTest, SignAndStoreSettings) { ...@@ -271,7 +271,7 @@ TEST_F(SessionManagerOperationTest, SignAndStoreSettings) {
validator->ValidateSignature( validator->ValidateSignature(
public_key_as_string, public_key_as_string,
policy::GetPolicyVerificationKey(), policy::GetPolicyVerificationKey(),
policy::PolicyBuilder::GetTestSigningKeySignature(), policy::PolicyBuilder::kFakeDomain,
false); false);
validator->StartValidation( validator->StartValidation(
base::Bind(&SessionManagerOperationTest::CheckSuccessfulValidation, base::Bind(&SessionManagerOperationTest::CheckSuccessfulValidation,
......
...@@ -113,22 +113,34 @@ void CloudPolicyValidatorBase::ValidatePayload() { ...@@ -113,22 +113,34 @@ void CloudPolicyValidatorBase::ValidatePayload() {
validation_flags_ |= VALIDATE_PAYLOAD; validation_flags_ |= VALIDATE_PAYLOAD;
} }
void CloudPolicyValidatorBase::ValidateCachedKey(
const std::string& cached_key,
const std::string& cached_key_signature,
const std::string& verification_key,
const std::string& owning_domain) {
validation_flags_ |= VALIDATE_CACHED_KEY;
set_verification_key_and_domain(verification_key, owning_domain);
cached_key_ = cached_key;
cached_key_signature_ = cached_key_signature;
}
void CloudPolicyValidatorBase::ValidateSignature( void CloudPolicyValidatorBase::ValidateSignature(
const std::string& key, const std::string& key,
const std::string& verification_key, const std::string& verification_key,
const std::string& key_signature, const std::string& owning_domain,
bool allow_key_rotation) { bool allow_key_rotation) {
validation_flags_ |= VALIDATE_SIGNATURE; validation_flags_ |= VALIDATE_SIGNATURE;
set_verification_key(verification_key); set_verification_key_and_domain(verification_key, owning_domain);
key_ = key; key_ = key;
key_signature_ = key_signature;
allow_key_rotation_ = allow_key_rotation; allow_key_rotation_ = allow_key_rotation;
} }
void CloudPolicyValidatorBase::ValidateInitialKey( void CloudPolicyValidatorBase::ValidateInitialKey(
const std::string& verification_key) { const std::string& verification_key,
const std::string& owning_domain) {
validation_flags_ |= VALIDATE_INITIAL_KEY; validation_flags_ |= VALIDATE_INITIAL_KEY;
set_verification_key(verification_key); set_verification_key_and_domain(verification_key, owning_domain);
} }
void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy( void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
...@@ -228,6 +240,7 @@ void CloudPolicyValidatorBase::RunChecks() { ...@@ -228,6 +240,7 @@ void CloudPolicyValidatorBase::RunChecks() {
} kCheckFunctions[] = { } kCheckFunctions[] = {
{ VALIDATE_SIGNATURE, &CloudPolicyValidatorBase::CheckSignature }, { VALIDATE_SIGNATURE, &CloudPolicyValidatorBase::CheckSignature },
{ VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey }, { VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
{ VALIDATE_CACHED_KEY, &CloudPolicyValidatorBase::CheckCachedKey },
{ VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType }, { VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
{ VALIDATE_ENTITY_ID, &CloudPolicyValidatorBase::CheckEntityId }, { VALIDATE_ENTITY_ID, &CloudPolicyValidatorBase::CheckEntityId },
{ VALIDATE_TOKEN, &CloudPolicyValidatorBase::CheckToken }, { VALIDATE_TOKEN, &CloudPolicyValidatorBase::CheckToken },
...@@ -292,16 +305,46 @@ bool CloudPolicyValidatorBase::CheckVerificationKeySignature( ...@@ -292,16 +305,46 @@ bool CloudPolicyValidatorBase::CheckVerificationKeySignature(
const std::string& key, const std::string& key,
const std::string& verification_key, const std::string& verification_key,
const std::string& signature) { const std::string& signature) {
// TODO(atwilson): Update this routine to include the domain name in the DCHECK(!verification_key.empty());
// signed data. em::PolicyPublicKeyAndDomain signed_data;
return VerifySignature(key, verification_key, signature, SHA256); signed_data.set_new_public_key(key);
// If no owning_domain_ supplied, try extracting the domain from the policy
// itself (this happens on certain platforms during startup, when we validate
// cached policy before prefs are loaded).
std::string domain = owning_domain_.empty() ?
ExtractDomainFromPolicy() : owning_domain_;
if (domain.empty()) {
LOG(ERROR) << "Policy does not contain a domain";
return false;
}
signed_data.set_domain(domain);
std::string signed_data_as_string;
if (!signed_data.SerializeToString(&signed_data_as_string)) {
DLOG(ERROR) << "Could not serialize verification key to string";
return false;
}
return VerifySignature(signed_data_as_string, verification_key, signature,
SHA256);
}
std::string CloudPolicyValidatorBase::ExtractDomainFromPolicy() {
std::string domain;
if (policy_data_->has_username()) {
domain = gaia::ExtractDomainName(
gaia::CanonicalizeEmail(
gaia::SanitizeEmail(policy_data_->username())));
}
return domain;
} }
void CloudPolicyValidatorBase::set_verification_key( void CloudPolicyValidatorBase::set_verification_key_and_domain(
const std::string& verification_key) { const std::string& verification_key, const std::string& owning_domain) {
// Make sure we aren't overwriting the verification key with a different key. // Make sure we aren't overwriting the verification key with a different key.
DCHECK(verification_key_.empty() || verification_key_ == verification_key); DCHECK(verification_key_.empty() || verification_key_ == verification_key);
DCHECK(owning_domain_.empty() || owning_domain_ == owning_domain);
verification_key_ = verification_key; verification_key_ = verification_key;
owning_domain_ = owning_domain;
} }
CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() { CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
...@@ -328,16 +371,6 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() { ...@@ -328,16 +371,6 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
return VALIDATION_BAD_SIGNATURE; return VALIDATION_BAD_SIGNATURE;
} }
// If a key verification signature is available, then verify the base signing
// key as well.
if (!key_signature_.empty() && !verification_key_.empty() &&
!CheckVerificationKeySignature(key_, verification_key_, key_signature_)) {
LOG(ERROR) << "Verification key signature verification failed";
return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
} else {
DVLOG(1) << "Verification key signature verification succeeded";
}
return VALIDATION_OK; return VALIDATION_OK;
} }
...@@ -357,6 +390,18 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() { ...@@ -357,6 +390,18 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
return VALIDATION_OK; return VALIDATION_OK;
} }
CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckCachedKey() {
if (!cached_key_signature_.empty() && !verification_key_.empty() &&
!CheckVerificationKeySignature(cached_key_, verification_key_,
cached_key_signature_)) {
LOG(ERROR) << "Cached key signature verification failed";
return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE;
} else {
DVLOG(1) << "Cached key signature verification succeeded";
}
return VALIDATION_OK;
}
CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() { CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckPolicyType() {
if (!policy_data_->has_policy_type() || if (!policy_data_->has_policy_type() ||
policy_data_->policy_type() != policy_type_) { policy_data_->policy_type() != policy_type_) {
...@@ -440,18 +485,13 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() { ...@@ -440,18 +485,13 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
return VALIDATION_OK; return VALIDATION_OK;
} }
CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() { CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() {
if (!policy_data_->has_username()) { std::string policy_domain = ExtractDomainFromPolicy();
if (policy_domain.empty()) {
LOG(ERROR) << "Policy is missing user name"; LOG(ERROR) << "Policy is missing user name";
return VALIDATION_BAD_USERNAME; return VALIDATION_BAD_USERNAME;
} }
std::string policy_domain =
gaia::ExtractDomainName(
gaia::CanonicalizeEmail(
gaia::SanitizeEmail(policy_data_->username())));
if (domain_ != policy_domain) { if (domain_ != policy_domain) {
LOG(ERROR) << "Invalid user name " << policy_data_->username(); LOG(ERROR) << "Invalid user name " << policy_data_->username();
return VALIDATION_BAD_USERNAME; return VALIDATION_BAD_USERNAME;
......
...@@ -147,16 +147,23 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -147,16 +147,23 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// Validates that the payload can be decoded successfully. // Validates that the payload can be decoded successfully.
void ValidatePayload(); void ValidatePayload();
// Verifies that |cached_key| is valid, by verifying the
// |cached_key_signature| using the passed |owning_domain| and
// |verification_key|.
void ValidateCachedKey(const std::string& cached_key,
const std::string& cached_key_signature,
const std::string& verification_key,
const std::string& owning_domain);
// 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 // |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, this checks the signature on the new key against |key| and the
// policy blob against the new key. New key is also validated using the passed // policy blob against the new key. New key is also validated using the passed
// |verification_key| and the |new_public_key_verification_signature| field. // |verification_key| and |owning_domain|, and the
// If |key_signature| is non-empty, then |key| is also verified against that // |new_public_key_verification_signature| field.
// signature (useful when dealing with cached keys from untrusted sources).
void ValidateSignature(const std::string& key, void ValidateSignature(const std::string& key,
const std::string& verification_key, const std::string& verification_key,
const std::string& key_signature, const std::string& owning_domain,
bool allow_key_rotation); bool allow_key_rotation);
// Similar to ValidateSignature(), this checks the signature on the // Similar to ValidateSignature(), this checks the signature on the
...@@ -165,7 +172,8 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -165,7 +172,8 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// be called at setup time when there is no existing policy key present to // be called at setup time when there is no existing policy key present to
// check against. New key is validated using the passed |verification_key| and // check against. New key is validated using the passed |verification_key| and
// the new_public_key_verification_signature field. // the new_public_key_verification_signature field.
void ValidateInitialKey(const std::string& verification_key); void ValidateInitialKey(const std::string& verification_key,
const std::string& owning_domain);
// Convenience helper that configures timestamp and token validation based on // Convenience helper that configures timestamp and token validation based on
// the current policy blob. |policy_data| may be NULL, in which case the // the current policy blob. |policy_data| may be NULL, in which case the
...@@ -205,6 +213,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -205,6 +213,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
VALIDATE_PAYLOAD = 1 << 6, VALIDATE_PAYLOAD = 1 << 6,
VALIDATE_SIGNATURE = 1 << 7, VALIDATE_SIGNATURE = 1 << 7,
VALIDATE_INITIAL_KEY = 1 << 8, VALIDATE_INITIAL_KEY = 1 << 8,
VALIDATE_CACHED_KEY = 1 << 9,
}; };
enum SignatureType { enum SignatureType {
...@@ -236,9 +245,14 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -236,9 +245,14 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
const std::string& server_key, const std::string& server_key,
const std::string& signature); const std::string& signature);
// Sets the key used to verify new public keys, and ensures that callers // Returns the domain name from the policy being validated. Returns an
// don't try to set conflicting keys. // empty string if the policy does not contain a username field.
void set_verification_key(const std::string& verification_key); std::string ExtractDomainFromPolicy();
// Sets the key and domain used to verify new public keys, and ensures that
// callers don't try to set conflicting values.
void set_verification_key_and_domain(const std::string& verification_key,
const std::string& owning_domain);
// Helper functions implementing individual checks. // Helper functions implementing individual checks.
Status CheckTimestamp(); Status CheckTimestamp();
...@@ -250,6 +264,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -250,6 +264,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
Status CheckPayload(); Status CheckPayload();
Status CheckSignature(); Status CheckSignature();
Status CheckInitialKey(); Status CheckInitialKey();
Status CheckCachedKey();
// Verifies the SHA1/ or SHA256/RSA |signature| on |data| against |key|. // Verifies the SHA1/ or SHA256/RSA |signature| on |data| against |key|.
// |signature_type| specifies the type of signature (SHA1 or SHA256). // |signature_type| specifies the type of signature (SHA1 or SHA256).
...@@ -274,8 +289,10 @@ class POLICY_EXPORT CloudPolicyValidatorBase { ...@@ -274,8 +289,10 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
std::string policy_type_; std::string policy_type_;
std::string settings_entity_id_; std::string settings_entity_id_;
std::string key_; std::string key_;
std::string key_signature_; std::string cached_key_;
std::string cached_key_signature_;
std::string verification_key_; std::string verification_key_;
std::string owning_domain_;
bool allow_key_rotation_; bool allow_key_rotation_;
scoped_refptr<base::SequencedTaskRunner> background_task_runner_; scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "components/policy/core/common/cloud/policy_builder.h" #include "components/policy/core/common/cloud/policy_builder.h"
#include "components/policy/core/common/policy_switches.h" #include "components/policy/core/common/policy_switches.h"
#include "crypto/rsa_private_key.h" #include "crypto/rsa_private_key.h"
#include "policy/proto/device_management_backend.pb.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -41,7 +42,8 @@ class CloudPolicyValidatorTest : public testing::Test { ...@@ -41,7 +42,8 @@ class CloudPolicyValidatorTest : public testing::Test {
timestamp_option_(CloudPolicyValidatorBase::TIMESTAMP_REQUIRED), timestamp_option_(CloudPolicyValidatorBase::TIMESTAMP_REQUIRED),
ignore_missing_dm_token_(CloudPolicyValidatorBase::DM_TOKEN_REQUIRED), ignore_missing_dm_token_(CloudPolicyValidatorBase::DM_TOKEN_REQUIRED),
allow_key_rotation_(true), allow_key_rotation_(true),
existing_dm_token_(PolicyBuilder::kFakeToken) { existing_dm_token_(PolicyBuilder::kFakeToken),
owning_domain_(PolicyBuilder::kFakeDomain){
policy_.SetDefaultNewSigningKey(); policy_.SetDefaultNewSigningKey();
} }
...@@ -51,8 +53,16 @@ class CloudPolicyValidatorTest : public testing::Test { ...@@ -51,8 +53,16 @@ class CloudPolicyValidatorTest : public testing::Test {
} }
void Validate(testing::Action<void(UserCloudPolicyValidator*)> check_action) { void Validate(testing::Action<void(UserCloudPolicyValidator*)> check_action) {
policy_.Build();
ValidatePolicy(check_action, policy_.GetCopy());
}
void ValidatePolicy(
testing::Action<void(UserCloudPolicyValidator*)> check_action,
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response) {
// Create a validator. // Create a validator.
scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(); scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
policy_response.Pass());
// Run validation and check the result. // Run validation and check the result.
EXPECT_CALL(*this, ValidationCompletion(validator.get())).WillOnce( EXPECT_CALL(*this, ValidationCompletion(validator.get())).WillOnce(
...@@ -64,12 +74,12 @@ class CloudPolicyValidatorTest : public testing::Test { ...@@ -64,12 +74,12 @@ class CloudPolicyValidatorTest : public testing::Test {
Mock::VerifyAndClearExpectations(this); Mock::VerifyAndClearExpectations(this);
} }
scoped_ptr<UserCloudPolicyValidator> CreateValidator() { scoped_ptr<UserCloudPolicyValidator> CreateValidator(
scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response) {
std::vector<uint8> public_key_bytes; std::vector<uint8> public_key_bytes;
EXPECT_TRUE( EXPECT_TRUE(
PolicyBuilder::CreateTestSigningKey()->ExportPublicKey( PolicyBuilder::CreateTestSigningKey()->ExportPublicKey(
&public_key_bytes)); &public_key_bytes));
policy_.Build();
// Convert from bytes to string format (which is what ValidateSignature() // Convert from bytes to string format (which is what ValidateSignature()
// takes). // takes).
...@@ -78,20 +88,25 @@ class CloudPolicyValidatorTest : public testing::Test { ...@@ -78,20 +88,25 @@ class CloudPolicyValidatorTest : public testing::Test {
public_key_bytes.size()); public_key_bytes.size());
UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create( UserCloudPolicyValidator* validator = UserCloudPolicyValidator::Create(
policy_.GetCopy(), base::MessageLoopProxy::current()); policy_response.Pass(), base::MessageLoopProxy::current());
validator->ValidateTimestamp(timestamp_, timestamp_, validator->ValidateTimestamp(timestamp_, timestamp_,
timestamp_option_); timestamp_option_);
validator->ValidateUsername(PolicyBuilder::kFakeUsername); validator->ValidateUsername(PolicyBuilder::kFakeUsername);
validator->ValidateDomain(PolicyBuilder::kFakeDomain); if (!owning_domain_.empty())
validator->ValidateDomain(owning_domain_);
validator->ValidateDMToken(existing_dm_token_, ignore_missing_dm_token_); validator->ValidateDMToken(existing_dm_token_, ignore_missing_dm_token_);
validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType); validator->ValidatePolicyType(dm_protocol::kChromeUserPolicyType);
validator->ValidatePayload(); validator->ValidatePayload();
validator->ValidateCachedKey(public_key,
PolicyBuilder::GetTestSigningKeySignature(),
GetPolicyVerificationKey(),
owning_domain_);
validator->ValidateSignature(public_key, validator->ValidateSignature(public_key,
GetPolicyVerificationKey(), GetPolicyVerificationKey(),
PolicyBuilder::GetTestSigningKeySignature(), owning_domain_,
allow_key_rotation_); allow_key_rotation_);
if (allow_key_rotation_) if (allow_key_rotation_)
validator->ValidateInitialKey(GetPolicyVerificationKey()); validator->ValidateInitialKey(GetPolicyVerificationKey(), owning_domain_);
return make_scoped_ptr(validator); return make_scoped_ptr(validator);
} }
...@@ -113,6 +128,7 @@ class CloudPolicyValidatorTest : public testing::Test { ...@@ -113,6 +128,7 @@ class CloudPolicyValidatorTest : public testing::Test {
std::string signing_key_; std::string signing_key_;
bool allow_key_rotation_; bool allow_key_rotation_;
std::string existing_dm_token_; std::string existing_dm_token_;
std::string owning_domain_;
UserPolicyBuilder policy_; UserPolicyBuilder policy_;
...@@ -127,7 +143,9 @@ TEST_F(CloudPolicyValidatorTest, SuccessfulValidation) { ...@@ -127,7 +143,9 @@ TEST_F(CloudPolicyValidatorTest, SuccessfulValidation) {
} }
TEST_F(CloudPolicyValidatorTest, SuccessfulRunValidation) { TEST_F(CloudPolicyValidatorTest, SuccessfulRunValidation) {
scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(); policy_.Build();
scoped_ptr<UserCloudPolicyValidator> validator = CreateValidator(
policy_.GetCopy());
// Run validation immediately (no background tasks). // Run validation immediately (no background tasks).
validator->RunValidation(); validator->RunValidation();
CheckSuccessfulValidation(validator.get()); CheckSuccessfulValidation(validator.get());
...@@ -240,7 +258,7 @@ TEST_F(CloudPolicyValidatorTest, ErrorNoUsername) { ...@@ -240,7 +258,7 @@ TEST_F(CloudPolicyValidatorTest, ErrorNoUsername) {
} }
TEST_F(CloudPolicyValidatorTest, ErrorInvalidUsername) { TEST_F(CloudPolicyValidatorTest, ErrorInvalidUsername) {
policy_.policy_data().set_username("invalid"); policy_.policy_data().set_username("invalid@example.com");
Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_USERNAME)); Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_USERNAME));
} }
...@@ -302,12 +320,42 @@ TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeySignature) { ...@@ -302,12 +320,42 @@ TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeySignature) {
// Validation key is not currently checked on Chrome OS // Validation key is not currently checked on Chrome OS
// (http://crbug.com/328038). // (http://crbug.com/328038).
TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeyVerificationSignature) { TEST_F(CloudPolicyValidatorTest, ErrorInvalidPublicKeyVerificationSignature) {
policy_.Build();
policy_.policy().set_new_public_key_verification_signature("invalid"); policy_.policy().set_new_public_key_verification_signature("invalid");
Validate(CheckStatus( ValidatePolicy(CheckStatus(
CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE)); CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
policy_.GetCopy());
}
TEST_F(CloudPolicyValidatorTest, ErrorDomainMismatchForKeyVerification) {
policy_.Build();
// Generate a non-matching owning_domain, which should cause a validation
// failure.
owning_domain_ = "invalid.com";
ValidatePolicy(CheckStatus(
CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
policy_.GetCopy());
}
TEST_F(CloudPolicyValidatorTest, ErrorDomainExtractedFromUsernameMismatch) {
// Generate a non-matching username domain, which should cause a validation
// failure when we try to verify the signing key with it.
policy_.policy_data().set_username("wonky@invalid.com");
policy_.Build();
owning_domain_ = "";
ValidatePolicy(CheckStatus(
CloudPolicyValidatorBase::VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE),
policy_.GetCopy());
} }
#endif #endif
TEST_F(CloudPolicyValidatorTest, SuccessfulNoDomainValidation) {
// Don't pass in a domain - this tells the validation code to instead
// extract the domain from the username.
owning_domain_ = "";
Validate(Invoke(this, &CloudPolicyValidatorTest::CheckSuccessfulValidation));
}
TEST_F(CloudPolicyValidatorTest, ErrorNoRotationAllowed) { TEST_F(CloudPolicyValidatorTest, ErrorNoRotationAllowed) {
allow_key_rotation_ = false; allow_key_rotation_ = false;
Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE)); Validate(CheckStatus(CloudPolicyValidatorBase::VALIDATION_BAD_SIGNATURE));
......
...@@ -49,6 +49,32 @@ const uint8 kSigningKey[] = { ...@@ -49,6 +49,32 @@ const uint8 kSigningKey[] = {
0x98, 0x68, 0xe1, 0x04, 0xa8, 0x92, 0xd0, 0x10, 0xaa, 0x98, 0x68, 0xe1, 0x04, 0xa8, 0x92, 0xd0, 0x10, 0xaa,
}; };
// SHA256 signature of kSigningKey for "example.com" domain.
const uint8 kSigningKeySignature[] = {
0x97, 0xEB, 0x13, 0xE6, 0x6C, 0xE2, 0x7A, 0x2F, 0xC6, 0x6E, 0x68, 0x8F,
0xED, 0x5B, 0x51, 0x08, 0x27, 0xF0, 0xA5, 0x97, 0x20, 0xEE, 0xE2, 0x9B,
0x5B, 0x63, 0xA5, 0x9C, 0xAE, 0x41, 0xFD, 0x34, 0xC4, 0x2E, 0xEB, 0x63,
0x10, 0x80, 0x0C, 0x74, 0x77, 0x6E, 0x34, 0x1C, 0x1B, 0x3B, 0x8E, 0x2A,
0x3A, 0x7F, 0xF9, 0x73, 0xB6, 0x2B, 0xB6, 0x45, 0xDB, 0x05, 0xE8, 0x5A,
0x68, 0x36, 0x05, 0x3C, 0x62, 0x3A, 0x6C, 0x64, 0xDB, 0x0E, 0x61, 0xBD,
0x29, 0x1C, 0x61, 0x4B, 0xE0, 0xDA, 0x07, 0xBA, 0x29, 0x81, 0xF0, 0x90,
0x58, 0xB8, 0xBB, 0xF4, 0x69, 0xFF, 0x8F, 0x2B, 0x4A, 0x2D, 0x98, 0x51,
0x37, 0xF5, 0x52, 0xCB, 0xE3, 0xC4, 0x6D, 0xEC, 0xEA, 0x32, 0x2D, 0xDD,
0xD7, 0xFC, 0x43, 0xC6, 0x54, 0xE1, 0xC1, 0x66, 0x43, 0x37, 0x09, 0xE1,
0xBF, 0xD1, 0x11, 0xFC, 0xDB, 0xBF, 0xDF, 0x66, 0x53, 0x8F, 0x38, 0x2D,
0xAA, 0x89, 0xD2, 0x9F, 0x60, 0x90, 0xB7, 0x05, 0xC2, 0x20, 0x82, 0xE6,
0xE0, 0x57, 0x55, 0xFF, 0x5F, 0xC1, 0x76, 0x66, 0x46, 0xF8, 0x67, 0xB8,
0x8B, 0x81, 0x53, 0xA9, 0x8B, 0x48, 0x9E, 0x2A, 0xF9, 0x60, 0x57, 0xBA,
0xD7, 0x52, 0x97, 0x53, 0xF0, 0x2F, 0x78, 0x68, 0x50, 0x18, 0x12, 0x00,
0x5E, 0x8E, 0x2A, 0x62, 0x0D, 0x48, 0xA9, 0xB5, 0x6B, 0xBC, 0xA0, 0x52,
0x53, 0xD7, 0x65, 0x23, 0xA4, 0xA5, 0xF5, 0x32, 0x49, 0x2D, 0xB2, 0x77,
0x2C, 0x66, 0x97, 0xBA, 0x58, 0xE0, 0x16, 0x1C, 0x8C, 0x02, 0x5D, 0xE0,
0x73, 0x2E, 0xDF, 0xB4, 0x2F, 0x4C, 0xA2, 0x11, 0x26, 0xC1, 0xAF, 0xAC,
0x73, 0xBC, 0xB6, 0x98, 0xE0, 0x20, 0x61, 0x0E, 0x52, 0x4A, 0x6C, 0x80,
0xB5, 0x0C, 0x10, 0x80, 0x09, 0x17, 0xF4, 0x9D, 0xFE, 0xB5, 0xFC, 0x63,
0x9A, 0x80, 0x3F, 0x76,
};
// New signing key test data in DER-encoded PKCS8 format. // New signing key test data in DER-encoded PKCS8 format.
const uint8 kNewSigningKey[] = { const uint8 kNewSigningKey[] = {
0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
...@@ -82,6 +108,32 @@ const uint8 kNewSigningKey[] = { ...@@ -82,6 +108,32 @@ const uint8 kNewSigningKey[] = {
0x32, 0x1a, 0x6b, 0xb3, 0x5f, 0x35, 0xbd, 0xf3, 0x32, 0x1a, 0x6b, 0xb3, 0x5f, 0x35, 0xbd, 0xf3,
}; };
// SHA256 signature of kNewSigningKey for "example.com" domain.
const uint8 kNewSigningKeySignature[] = {
0x70, 0xED, 0x27, 0x42, 0x34, 0x69, 0xB6, 0x47, 0x9E, 0x7C, 0xA0, 0xF0,
0xE5, 0x0A, 0x49, 0x49, 0x00, 0xDA, 0xBC, 0x70, 0x01, 0xC5, 0x4B, 0xDB,
0x47, 0xD5, 0xAF, 0xA1, 0xAD, 0xB7, 0xE4, 0xE1, 0xBD, 0x5A, 0x1C, 0x35,
0x44, 0x5A, 0xAA, 0xDB, 0x27, 0xBA, 0xA4, 0xA9, 0xC8, 0xDD, 0xEC, 0xD6,
0xEB, 0xFE, 0xDB, 0xE0, 0x03, 0x5C, 0xA6, 0x2E, 0x5A, 0xEC, 0x75, 0x79,
0xB8, 0x5F, 0x0A, 0xEE, 0x05, 0xB2, 0x61, 0xDC, 0x58, 0xF0, 0xD1, 0xCB,
0x7B, 0x2A, 0xDB, 0xC1, 0x7C, 0x60, 0xE6, 0x3E, 0x87, 0x02, 0x61, 0xE6,
0x90, 0xFD, 0x54, 0x65, 0xC7, 0xFF, 0x74, 0x09, 0xD6, 0xAA, 0x8E, 0xDC,
0x5B, 0xC8, 0x38, 0x0C, 0x84, 0x0E, 0x84, 0x2E, 0x37, 0x2A, 0x4B, 0xDE,
0x31, 0x82, 0x76, 0x1E, 0x77, 0xA5, 0xC1, 0xD5, 0xED, 0xFF, 0xBC, 0xEA,
0x91, 0xB7, 0xBC, 0xFF, 0x76, 0x23, 0xE2, 0x78, 0x63, 0x01, 0x47, 0x80,
0x47, 0x1F, 0x3A, 0x49, 0xBF, 0x0D, 0xCF, 0x27, 0x70, 0x92, 0xBB, 0xEA,
0xB3, 0x92, 0x70, 0xFF, 0x1E, 0x4B, 0x1B, 0xE0, 0x4E, 0x0C, 0x4C, 0x6B,
0x5D, 0x77, 0x06, 0xBB, 0xFB, 0x9B, 0x0E, 0x55, 0xB8, 0x8A, 0xF2, 0x45,
0xA9, 0xF3, 0x54, 0x3D, 0x0C, 0xAC, 0xA8, 0x15, 0xD2, 0x31, 0x8D, 0x97,
0x08, 0x73, 0xC9, 0x0F, 0x1D, 0xDE, 0x10, 0x22, 0xC6, 0x55, 0x53, 0x7F,
0x7C, 0x50, 0x16, 0x5A, 0x08, 0xCC, 0x1C, 0x53, 0x9B, 0x02, 0xB8, 0x80,
0xB7, 0x46, 0xF5, 0xF1, 0xC7, 0x3D, 0x36, 0xBD, 0x26, 0x02, 0xDE, 0x10,
0xAB, 0x5A, 0x03, 0xCD, 0x67, 0x00, 0x1C, 0x23, 0xC7, 0x13, 0xEE, 0x5D,
0xAF, 0xC5, 0x1F, 0xE3, 0xA0, 0x54, 0xAC, 0xC2, 0xC9, 0x44, 0xD4, 0x4A,
0x09, 0x8E, 0xEB, 0xAE, 0xCA, 0x08, 0x8A, 0x7F, 0x41, 0x7B, 0xD8, 0x2C,
0xDD, 0x6F, 0x80, 0xC3,
};
} // namespace } // namespace
// Constants used as dummy data for filling the PolicyData protobuf. // Constants used as dummy data for filling the PolicyData protobuf.
...@@ -142,17 +194,20 @@ void PolicyBuilder::SetDefaultNewSigningKey() { ...@@ -142,17 +194,20 @@ void PolicyBuilder::SetDefaultNewSigningKey() {
std::vector<uint8> key(kNewSigningKey, std::vector<uint8> key(kNewSigningKey,
kNewSigningKey + arraysize(kNewSigningKey)); kNewSigningKey + arraysize(kNewSigningKey));
raw_new_signing_key_.swap(key); raw_new_signing_key_.swap(key);
raw_new_signing_key_signature_ = GetTestOtherSigningKeySignature();
} }
void PolicyBuilder::SetDefaultInitialSigningKey() { void PolicyBuilder::SetDefaultInitialSigningKey() {
std::vector<uint8> key(kSigningKey, std::vector<uint8> key(kSigningKey,
kSigningKey + arraysize(kSigningKey)); kSigningKey + arraysize(kSigningKey));
raw_new_signing_key_.swap(key); raw_new_signing_key_.swap(key);
raw_new_signing_key_signature_ = GetTestSigningKeySignature();
UnsetSigningKey(); UnsetSigningKey();
} }
void PolicyBuilder::UnsetNewSigningKey() { void PolicyBuilder::UnsetNewSigningKey() {
raw_new_signing_key_.clear(); raw_new_signing_key_.clear();
raw_new_signing_key_signature_.clear();
} }
void PolicyBuilder::Build() { void PolicyBuilder::Build() {
...@@ -168,6 +223,9 @@ void PolicyBuilder::Build() { ...@@ -168,6 +223,9 @@ void PolicyBuilder::Build() {
policy_.set_new_public_key(vector_as_array(&raw_new_public_signing_key), policy_.set_new_public_key(vector_as_array(&raw_new_public_signing_key),
raw_new_public_signing_key.size()); raw_new_public_signing_key.size());
policy_.set_new_public_key_verification_signature(
raw_new_signing_key_signature_);
// The new public key must be signed by the old key. // The new public key must be signed by the old key.
scoped_ptr<crypto::RSAPrivateKey> old_signing_key = GetSigningKey(); scoped_ptr<crypto::RSAPrivateKey> old_signing_key = GetSigningKey();
if (old_signing_key) { if (old_signing_key) {
...@@ -218,14 +276,14 @@ scoped_ptr<crypto::RSAPrivateKey> PolicyBuilder::CreateTestOtherSigningKey() { ...@@ -218,14 +276,14 @@ scoped_ptr<crypto::RSAPrivateKey> PolicyBuilder::CreateTestOtherSigningKey() {
// static // static
std::string PolicyBuilder::GetTestSigningKeySignature() { std::string PolicyBuilder::GetTestSigningKeySignature() {
// TODO(atwilson): Return a real verification signature when one is available. return std::string(reinterpret_cast<const char*>(kSigningKeySignature),
return std::string(); sizeof(kSigningKeySignature));
} }
// static // static
std::string PolicyBuilder::GetTestOtherSigningKeySignature() { std::string PolicyBuilder::GetTestOtherSigningKeySignature() {
// TODO(atwilson): Return a real verification signature when one is available. return std::string(reinterpret_cast<const char*>(kNewSigningKeySignature),
return std::string(); sizeof(kNewSigningKeySignature));
} }
void PolicyBuilder::SignData(const std::string& data, void PolicyBuilder::SignData(const std::string& data,
......
...@@ -89,6 +89,9 @@ class PolicyBuilder { ...@@ -89,6 +89,9 @@ class PolicyBuilder {
static std::string GetTestSigningKeySignature(); static std::string GetTestSigningKeySignature();
static std::string GetTestOtherSigningKeySignature(); static std::string GetTestOtherSigningKeySignature();
std::vector<uint8> raw_signing_key() { return raw_signing_key_; }
std::vector<uint8> raw_new_signing_key() { return raw_new_signing_key_; }
private: private:
// Produces |key|'s signature over |data| and stores it in |signature|. // Produces |key|'s signature over |data| and stores it in |signature|.
void SignData(const std::string& data, void SignData(const std::string& data,
...@@ -107,6 +110,7 @@ class PolicyBuilder { ...@@ -107,6 +110,7 @@ class PolicyBuilder {
// temporary RSAPrivateKey is created. // temporary RSAPrivateKey is created.
std::vector<uint8> raw_signing_key_; std::vector<uint8> raw_signing_key_;
std::vector<uint8> raw_new_signing_key_; std::vector<uint8> raw_new_signing_key_;
std::string raw_new_signing_key_signature_;
DISALLOW_COPY_AND_ASSIGN(PolicyBuilder); DISALLOW_COPY_AND_ASSIGN(PolicyBuilder);
}; };
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/location.h" #include "base/location.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/task_runner_util.h" #include "base/task_runner_util.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "policy/proto/cloud_policy.pb.h" #include "policy/proto/cloud_policy.pb.h"
#include "policy/proto/device_management_backend.pb.h" #include "policy/proto/device_management_backend.pb.h"
#include "policy/proto/policy_signing_key.pb.h" #include "policy/proto/policy_signing_key.pb.h"
...@@ -295,10 +296,22 @@ void UserCloudPolicyStore::Validate( ...@@ -295,10 +296,22 @@ void UserCloudPolicyStore::Validate(
policy.Pass(), policy.Pass(),
CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE); CloudPolicyValidatorBase::TIMESTAMP_NOT_BEFORE);
// Validate the username if the user is signed in. // Extract the owning domain from the signed-in user (if any is set yet).
// If there's no owning domain, then the code just ensures that the policy
// is self-consistent (that the keys are signed with the same domain that the
// username field in the policy contains). UserPolicySigninServerBase will
// verify that the username matches the signed in user once profile
// initialization is complete (http://crbug.com/342327).
std::string owning_domain;
// Validate the username if the user is signed in. The signin_username_ can
// be empty during initial policy load because this happens before the
// Prefs subsystem is initialized.
if (!signin_username_.empty()) { if (!signin_username_.empty()) {
DVLOG(1) << "Validating username: " << signin_username_; DVLOG(1) << "Validating username: " << signin_username_;
validator->ValidateUsername(signin_username_); validator->ValidateUsername(signin_username_);
owning_domain = gaia::ExtractDomainName(
gaia::CanonicalizeEmail(gaia::SanitizeEmail(signin_username_)));
} }
// There are 4 cases: // There are 4 cases:
...@@ -325,13 +338,17 @@ void UserCloudPolicyStore::Validate( ...@@ -325,13 +338,17 @@ void UserCloudPolicyStore::Validate(
// kMetricPolicyHasVerifiedCachedKey rises to a high enough level. // kMetricPolicyHasVerifiedCachedKey rises to a high enough level.
DLOG(WARNING) << "Allowing unsigned cached blob for migration"; DLOG(WARNING) << "Allowing unsigned cached blob for migration";
} else { } else {
// Case #2 - loading from cache with a cached key - just do normal // Case #2 - loading from cache with a cached key - validate the cached
// signature validation using this key. We're loading from cache so don't // key, then do normal policy data signature validation using the cached
// allow key rotation. // key. We're loading from cache so don't allow key rotation.
validator->ValidateCachedKey(cached_key->signing_key(),
cached_key->signing_key_signature(),
verification_key_,
owning_domain);
const bool no_rotation = false; const bool no_rotation = false;
validator->ValidateSignature(cached_key->signing_key(), validator->ValidateSignature(cached_key->signing_key(),
verification_key_, verification_key_,
cached_key->signing_key_signature(), owning_domain,
no_rotation); no_rotation);
} }
} else { } else {
...@@ -340,15 +357,15 @@ void UserCloudPolicyStore::Validate( ...@@ -340,15 +357,15 @@ void UserCloudPolicyStore::Validate(
if (policy_key_.empty()) { if (policy_key_.empty()) {
// Case #3 - no valid existing policy key, so this new policy fetch should // Case #3 - no valid existing policy key, so this new policy fetch should
// include an initial key provision. // include an initial key provision.
validator->ValidateInitialKey(verification_key_); validator->ValidateInitialKey(verification_key_, owning_domain);
} else { } else {
// Case #4 - verify new policy with existing key. We always allow key // Case #4 - verify new policy with existing key. We always allow key
// rotation - the verification key will prevent invalid policy from being // rotation - the verification key will prevent invalid policy from being
// injected. |policy_key_| is already known to be valid, so no // injected. |policy_key_| is already known to be valid, so no need to
// verification signature is passed in. // verify via ValidateCachedKey().
const bool allow_rotation = true; const bool allow_rotation = true;
validator->ValidateSignature( validator->ValidateSignature(
policy_key_, verification_key_, std::string(), allow_rotation); policy_key_, verification_key_, owning_domain, allow_rotation);
} }
} }
......
...@@ -297,13 +297,13 @@ message PolicyFetchResponse { ...@@ -297,13 +297,13 @@ message PolicyFetchResponse {
optional bytes new_public_key_signature = 6; optional bytes new_public_key_signature = 6;
// If new_public_key is specified, this field contains a signature // If new_public_key is specified, this field contains a signature
// of that key, signed using a key only available to DMServer. // of a PolicyPublicKeyAndDomain protobuf, signed using a key only
// The public key portion of this well-known key is embedded into the // available to DMServer. The public key portion of this well-known key is
// Chrome binary. The hash of that embedded key is passed to DMServer // embedded into the Chrome binary. The hash of that embedded key is passed
// as verification_key_hash field in PolicyFetchRequest. DMServer will // to DMServer as verification_key_hash field in PolicyFetchRequest. DMServer
// pick a private key on the server which matches the hash (matches public // will pick a private key on the server which matches the hash (matches
// key on the client). If DMServer is unable to find matching key, it will // public key on the client). If DMServer is unable to find matching key, it
// return an error instead of policy data. // will return an error instead of policy data.
// In case hash was not specified, DMServer will leave verification signature // In case hash was not specified, DMServer will leave verification signature
// field empty (legacy behavior). // field empty (legacy behavior).
// In addition to the checks between new_public_key // In addition to the checks between new_public_key
...@@ -313,6 +313,17 @@ message PolicyFetchResponse { ...@@ -313,6 +313,17 @@ message PolicyFetchResponse {
optional bytes new_public_key_verification_signature = 7; optional bytes new_public_key_verification_signature = 7;
} }
// Protobuf used to generate the new_public_key_verification_signature field.
message PolicyPublicKeyAndDomain {
// The public key to sign (taken from the |new_public_key| field in
// PolicyFetchResponse).
optional bytes new_public_key = 1;
// The domain associated with this key (should match the domain portion of
// the username field of the policy).
optional string domain = 2;
}
// Request from device to server for reading policies. // Request from device to server for reading policies.
message DevicePolicyRequest { message DevicePolicyRequest {
// The policy fetch request. If this field exists, the request must // The policy fetch request. If this field exists, the request must
......
...@@ -22,10 +22,12 @@ std::string CanonicalizeEmail(const std::string& email_address) { ...@@ -22,10 +22,12 @@ std::string CanonicalizeEmail(const std::string& email_address) {
std::vector<std::string> parts; std::vector<std::string> parts;
char at = '@'; char at = '@';
base::SplitString(email_address, at, &parts); base::SplitString(email_address, at, &parts);
if (parts.size() != 2U) if (parts.size() != 2U) {
NOTREACHED() << "expecting exactly one @, but got " << parts.size(); NOTREACHED() << "expecting exactly one @, but got " << parts.size()-1 <<
else if (parts[1] == kGmailDomain) // only strip '.' for gmail accounts. " : " << email_address;
} else if (parts[1] == kGmailDomain) { // only strip '.' for gmail accounts.
base::RemoveChars(parts[0], ".", &parts[0]); base::RemoveChars(parts[0], ".", &parts[0]);
}
std::string new_email = StringToLowerASCII(JoinString(parts, at)); std::string new_email = StringToLowerASCII(JoinString(parts, at));
VLOG(1) << "Canonicalized " << email_address << " to " << new_email; VLOG(1) << "Canonicalized " << email_address << " to " << new_email;
return new_email; return new_email;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment