Skip to content

Commit

Permalink
Update policy signature verification to include policy domain.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
atwilson@chromium.org committed Feb 14, 2014
1 parent d9a4c37 commit 4f4b60e
Show file tree
Hide file tree
Showing 15 changed files with 307 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ void DeviceCloudPolicyStoreChromeOS::Store(
scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
validator->ValidateSignature(owner_key->public_key_as_string(),
GetPolicyVerificationKey(),
std::string(),
install_attributes_->GetDomain(),
true);
validator->ValidateAgainstCurrentPolicy(
device_settings_service_->policy_data(),
Expand Down Expand Up @@ -77,7 +77,8 @@ void DeviceCloudPolicyStoreChromeOS::InstallInitialPolicy(
}

scoped_ptr<DeviceCloudPolicyValidator> validator(CreateValidator(policy));
validator->ValidateInitialKey(GetPolicyVerificationKey());
validator->ValidateInitialKey(GetPolicyVerificationKey(),
install_attributes_->GetDomain());
validator.release()->StartValidation(
base::Bind(&DeviceCloudPolicyStoreChromeOS::OnPolicyToStoreValidated,
weak_factory_.GetWeakPtr()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "base/bind.h"
#include "base/callback.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/session_manager_client.h"
#include "components/policy/core/common/cloud/device_management_service.h"
Expand Down Expand Up @@ -190,9 +192,11 @@ void DeviceLocalAccountPolicyStore::Validate(
: CloudPolicyValidatorBase::TIMESTAMP_NOT_REQUIRED,
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePayload();
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
validator->ValidateSignature(key->public_key_as_string(),
GetPolicyVerificationKey(),
std::string(),
connector->GetEnterpriseDomain(),
false);
validator.release()->StartValidation(callback);
}
Expand Down
17 changes: 14 additions & 3 deletions chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,24 @@ void EnrollmentHandlerChromeOS::OnPolicyFetched(CloudPolicyClient* client) {

validator->ValidateTimestamp(base::Time(), base::Time::NowFromSystemTime(),
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(),
CloudPolicyValidatorBase::DM_TOKEN_REQUIRED);
validator->ValidatePolicyType(dm_protocol::kChromeDevicePolicyType);
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(
base::Bind(&EnrollmentHandlerChromeOS::PolicyValidated,
weak_ptr_factory_.GetWeakPtr()));
Expand Down
14 changes: 10 additions & 4 deletions chrome/browser/chromeos/policy/user_cloud_policy_store_chromeos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ void SampleValidationFailure(ValidationFailure sample) {
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

// Helper class for loading legacy policy caches.
Expand Down Expand Up @@ -259,7 +264,7 @@ void UserCloudPolicyStoreChromeOS::LoadImmediately() {
validator->ValidateSignature(
policy_key_,
GetPolicyVerificationKey(),
std::string(), // No signature verification needed.
ExtractDomain(sanitized_username),
allow_rotation);
validator->RunValidation();
OnRetrievedPolicyValidated(validator.get());
Expand All @@ -273,12 +278,13 @@ void UserCloudPolicyStoreChromeOS::ValidatePolicyForStore(
CloudPolicyValidatorBase::TIMESTAMP_REQUIRED);
validator->ValidateUsername(username_);
if (policy_key_.empty()) {
validator->ValidateInitialKey(GetPolicyVerificationKey());
validator->ValidateInitialKey(GetPolicyVerificationKey(),
ExtractDomain(username_));
} else {
const bool allow_rotation = true;
validator->ValidateSignature(policy_key_,
GetPolicyVerificationKey(),
std::string(),
ExtractDomain(username_),
allow_rotation);
}

Expand Down Expand Up @@ -377,7 +383,7 @@ void UserCloudPolicyStoreChromeOS::ValidateRetrievedPolicy(
const bool allow_rotation = false;
validator->ValidateSignature(policy_key_,
GetPolicyVerificationKey(),
std::string(),
ExtractDomain(username_),
allow_rotation);
// Start validation. The Validator will delete itself once validation is
// complete.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ namespace {

const char kLegacyDeviceId[] = "legacy-device-id";
const char kLegacyToken[] = "legacy-token";
const char kSanitizedUsername[] = "0123456789ABCDEF0123456789ABCDEF012345678";
const char kSanitizedUsername[] =
"0123456789ABCDEF0123456789ABCDEF012345678@example.com";
const char kDefaultHomepage[] = "http://chromium.org";

ACTION_P2(SendSanitizedUsername, call_status, sanitized_username) {
Expand Down Expand Up @@ -599,7 +600,7 @@ TEST_F(UserCloudPolicyStoreChromeOSTest, LoadImmediatelyNoUserPolicyKey) {
.WillOnce(Return(policy_.GetBlob()));
EXPECT_CALL(cryptohome_client_,
BlockingGetSanitizedUsername(PolicyBuilder::kFakeUsername))
.WillOnce(Return("wrong"));
.WillOnce(Return("wrong@example.com"));

EXPECT_FALSE(store_->policy());
store_->LoadImmediately();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,10 @@ void SessionManagerOperation::ValidateDeviceSettings(
policy::CloudPolicyValidatorBase::DM_TOKEN_NOT_REQUIRED);
validator->ValidatePolicyType(policy::dm_protocol::kChromeDevicePolicyType);
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(),
policy::GetPolicyVerificationKey(),
std::string(), // No key validation check.
std::string(),
false);
validator->StartValidation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ TEST_F(SessionManagerOperationTest, SignAndStoreSettings) {
validator->ValidateSignature(
public_key_as_string,
policy::GetPolicyVerificationKey(),
policy::PolicyBuilder::GetTestSigningKeySignature(),
policy::PolicyBuilder::kFakeDomain,
false);
validator->StartValidation(
base::Bind(&SessionManagerOperationTest::CheckSuccessfulValidation,
Expand Down
94 changes: 67 additions & 27 deletions components/policy/core/common/cloud/cloud_policy_validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,34 @@ void CloudPolicyValidatorBase::ValidatePayload() {
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(
const std::string& key,
const std::string& verification_key,
const std::string& key_signature,
const std::string& owning_domain,
bool allow_key_rotation) {
validation_flags_ |= VALIDATE_SIGNATURE;
set_verification_key(verification_key);
set_verification_key_and_domain(verification_key, owning_domain);
key_ = key;
key_signature_ = key_signature;
allow_key_rotation_ = allow_key_rotation;
}

void CloudPolicyValidatorBase::ValidateInitialKey(
const std::string& verification_key) {
const std::string& verification_key,
const std::string& owning_domain) {
validation_flags_ |= VALIDATE_INITIAL_KEY;
set_verification_key(verification_key);
set_verification_key_and_domain(verification_key, owning_domain);
}

void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy(
Expand Down Expand Up @@ -228,6 +240,7 @@ void CloudPolicyValidatorBase::RunChecks() {
} kCheckFunctions[] = {
{ VALIDATE_SIGNATURE, &CloudPolicyValidatorBase::CheckSignature },
{ VALIDATE_INITIAL_KEY, &CloudPolicyValidatorBase::CheckInitialKey },
{ VALIDATE_CACHED_KEY, &CloudPolicyValidatorBase::CheckCachedKey },
{ VALIDATE_POLICY_TYPE, &CloudPolicyValidatorBase::CheckPolicyType },
{ VALIDATE_ENTITY_ID, &CloudPolicyValidatorBase::CheckEntityId },
{ VALIDATE_TOKEN, &CloudPolicyValidatorBase::CheckToken },
Expand Down Expand Up @@ -292,16 +305,46 @@ 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, SHA256);
DCHECK(!verification_key.empty());
em::PolicyPublicKeyAndDomain signed_data;
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(
const std::string& verification_key) {
void CloudPolicyValidatorBase::set_verification_key_and_domain(
const std::string& verification_key, const std::string& owning_domain) {
// Make sure we aren't overwriting the verification key with a different key.
DCHECK(verification_key_.empty() || verification_key_ == verification_key);
DCHECK(owning_domain_.empty() || owning_domain_ == owning_domain);
verification_key_ = verification_key;
owning_domain_ = owning_domain;
}

CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() {
Expand All @@ -328,16 +371,6 @@ 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() && !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;
}

Expand All @@ -357,6 +390,18 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() {
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() {
if (!policy_data_->has_policy_type() ||
policy_data_->policy_type() != policy_type_) {
Expand Down Expand Up @@ -440,18 +485,13 @@ CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUsername() {
return VALIDATION_OK;
}


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";
return VALIDATION_BAD_USERNAME;
}

std::string policy_domain =
gaia::ExtractDomainName(
gaia::CanonicalizeEmail(
gaia::SanitizeEmail(policy_data_->username())));

if (domain_ != policy_domain) {
LOG(ERROR) << "Invalid user name " << policy_data_->username();
return VALIDATION_BAD_USERNAME;
Expand Down
35 changes: 26 additions & 9 deletions components/policy/core/common/cloud/cloud_policy_validator.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,23 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// Validates that the payload can be decoded successfully.
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
// |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. 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).
// |verification_key| and |owning_domain|, and the
// |new_public_key_verification_signature| field.
void ValidateSignature(const std::string& key,
const std::string& verification_key,
const std::string& key_signature,
const std::string& owning_domain,
bool allow_key_rotation);

// Similar to ValidateSignature(), this checks the signature on the
Expand All @@ -165,7 +172,8 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
// 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
// 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
// the current policy blob. |policy_data| may be NULL, in which case the
Expand Down Expand Up @@ -205,6 +213,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
VALIDATE_PAYLOAD = 1 << 6,
VALIDATE_SIGNATURE = 1 << 7,
VALIDATE_INITIAL_KEY = 1 << 8,
VALIDATE_CACHED_KEY = 1 << 9,
};

enum SignatureType {
Expand Down Expand Up @@ -236,9 +245,14 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
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);
// Returns the domain name from the policy being validated. Returns an
// empty string if the policy does not contain a username field.
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.
Status CheckTimestamp();
Expand All @@ -250,6 +264,7 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
Status CheckPayload();
Status CheckSignature();
Status CheckInitialKey();
Status CheckCachedKey();

// Verifies the SHA1/ or SHA256/RSA |signature| on |data| against |key|.
// |signature_type| specifies the type of signature (SHA1 or SHA256).
Expand All @@ -274,8 +289,10 @@ class POLICY_EXPORT CloudPolicyValidatorBase {
std::string policy_type_;
std::string settings_entity_id_;
std::string key_;
std::string key_signature_;
std::string cached_key_;
std::string cached_key_signature_;
std::string verification_key_;
std::string owning_domain_;
bool allow_key_rotation_;
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;

Expand Down
Loading

0 comments on commit 4f4b60e

Please sign in to comment.