| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/policy/core/common/cloud/cloud_policy_validator.h" |
| |
| #include <stddef.h> |
| #include <utility> |
| |
| #include "base/bind_helpers.h" |
| #include "base/location.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "components/policy/core/common/cloud/cloud_policy_constants.h" |
| #include "components/policy/proto/device_management_backend.pb.h" |
| #include "crypto/signature_verifier.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| |
| namespace em = enterprise_management; |
| |
| namespace policy { |
| |
| namespace { |
| |
| const char kMetricPolicyKeyVerification[] = "Enterprise.PolicyKeyVerification"; |
| |
| enum MetricPolicyKeyVerification { |
| // Obsolete. Kept to avoid reuse, as this is used in histograms. |
| // UMA metric recorded when the client has no verification key. |
| METRIC_POLICY_KEY_VERIFICATION_KEY_MISSING_DEPRECATED, |
| // 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. |
| }; |
| |
| const char kMetricPolicyUserVerification[] = |
| "Enterprise.PolicyUserVerification"; |
| |
| enum class MetricPolicyUserVerification { |
| // Gaia id check used, but failed. |
| kGaiaIdFailed = 0, |
| // Gaia id check used and succeeded. |
| kGaiaIdSucceeded = 1, |
| // Gaia id is not present and username check failed. |
| kUsernameFailed = 2, |
| // Gaia id is not present for user and username check succeeded. |
| kUsernameSucceeded = 3, |
| // Gaia id is not present in policy and username check succeeded. |
| kGaiaIdMissingUsernameSucceeded = 4, |
| kMaxValue = kGaiaIdMissingUsernameSucceeded, |
| }; |
| |
| } // namespace |
| |
| // static |
| const char* CloudPolicyValidatorBase::StatusToString(Status status) { |
| switch (status) { |
| case VALIDATION_OK: |
| return "OK"; |
| case VALIDATION_BAD_INITIAL_SIGNATURE: |
| return "BAD_INITIAL_SIGNATURE"; |
| case VALIDATION_BAD_SIGNATURE: |
| return "BAD_SIGNATURE"; |
| case VALIDATION_ERROR_CODE_PRESENT: |
| return "ERROR_CODE_PRESENT"; |
| case VALIDATION_PAYLOAD_PARSE_ERROR: |
| return "PAYLOAD_PARSE_ERROR"; |
| case VALIDATION_WRONG_POLICY_TYPE: |
| return "WRONG_POLICY_TYPE"; |
| case VALIDATION_WRONG_SETTINGS_ENTITY_ID: |
| return "WRONG_SETTINGS_ENTITY_ID"; |
| case VALIDATION_BAD_TIMESTAMP: |
| return "BAD_TIMESTAMP"; |
| case VALIDATION_BAD_DM_TOKEN: |
| return "BAD_DM_TOKEN"; |
| case VALIDATION_BAD_DEVICE_ID: |
| return "BAD_DEVICE_ID"; |
| case VALIDATION_BAD_USER: |
| return "BAD_USER"; |
| case VALIDATION_POLICY_PARSE_ERROR: |
| return "POLICY_PARSE_ERROR"; |
| case VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE: |
| return "BAD_KEY_VERIFICATION_SIGNATURE"; |
| case VALIDATION_VALUE_WARNING: |
| return "VALUE_WARNING"; |
| case VALIDATION_VALUE_ERROR: |
| return "VALUE_ERROR"; |
| case VALIDATION_STATUS_SIZE: |
| return "UNKNOWN"; |
| } |
| return "UNKNOWN"; |
| } |
| |
| CloudPolicyValidatorBase::ValidationResult::ValidationResult() = default; |
| CloudPolicyValidatorBase::ValidationResult::~ValidationResult() = default; |
| |
| CloudPolicyValidatorBase::~CloudPolicyValidatorBase() {} |
| |
| std::unique_ptr<CloudPolicyValidatorBase::ValidationResult> |
| CloudPolicyValidatorBase::GetValidationResult() const { |
| std::unique_ptr<ValidationResult> result = |
| std::make_unique<ValidationResult>(); |
| result->status = status_; |
| result->value_validation_issues = value_validation_issues_; |
| result->policy_token = policy_data_->policy_token(); |
| result->policy_data_signature = policy_->policy_data_signature(); |
| return result; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateTimestamp( |
| base::Time not_before, |
| ValidateTimestampOption timestamp_option) { |
| validation_flags_ |= VALIDATE_TIMESTAMP; |
| timestamp_not_before_ = not_before.ToJavaTime(); |
| timestamp_option_ = timestamp_option; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateUser(const AccountId& account_id) { |
| validation_flags_ |= VALIDATE_USER; |
| account_id_ = account_id; |
| // Always canonicalize when falls back to username check, |
| // because it checks only for regular users. |
| canonicalize_user_ = true; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateUsername( |
| const std::string& expected_user, |
| bool canonicalize) { |
| validation_flags_ |= VALIDATE_USER; |
| account_id_ = AccountId::FromUserEmail(expected_user); |
| canonicalize_user_ = canonicalize; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateDomain( |
| const std::string& expected_domain) { |
| validation_flags_ |= VALIDATE_DOMAIN; |
| domain_ = gaia::CanonicalizeDomain(expected_domain); |
| } |
| |
| void CloudPolicyValidatorBase::ValidateDMToken( |
| const std::string& expected_dm_token, |
| ValidateDMTokenOption dm_token_option) { |
| validation_flags_ |= VALIDATE_DM_TOKEN; |
| dm_token_ = expected_dm_token; |
| dm_token_option_ = dm_token_option; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateDeviceId( |
| const std::string& expected_device_id, |
| ValidateDeviceIdOption device_id_option) { |
| validation_flags_ |= VALIDATE_DEVICE_ID; |
| device_id_ = expected_device_id; |
| device_id_option_ = device_id_option; |
| } |
| |
| void CloudPolicyValidatorBase::ValidatePolicyType( |
| const std::string& policy_type) { |
| validation_flags_ |= VALIDATE_POLICY_TYPE; |
| policy_type_ = policy_type; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateSettingsEntityId( |
| const std::string& settings_entity_id) { |
| validation_flags_ |= VALIDATE_ENTITY_ID; |
| settings_entity_id_ = settings_entity_id; |
| } |
| |
| void CloudPolicyValidatorBase::ValidatePayload() { |
| validation_flags_ |= VALIDATE_PAYLOAD; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateCachedKey( |
| const std::string& cached_key, |
| const std::string& cached_key_signature, |
| const std::string& owning_domain) { |
| validation_flags_ |= VALIDATE_CACHED_KEY; |
| set_owning_domain(owning_domain); |
| cached_key_ = cached_key; |
| cached_key_signature_ = cached_key_signature; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateSignature(const std::string& key) { |
| validation_flags_ |= VALIDATE_SIGNATURE; |
| DCHECK(key_.empty() || key_ == key); |
| key_ = key; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateSignatureAllowingRotation( |
| const std::string& key, |
| const std::string& owning_domain) { |
| validation_flags_ |= VALIDATE_SIGNATURE; |
| DCHECK(key_.empty() || key_ == key); |
| key_ = key; |
| set_owning_domain(owning_domain); |
| allow_key_rotation_ = true; |
| } |
| |
| void CloudPolicyValidatorBase::ValidateInitialKey( |
| const std::string& owning_domain) { |
| validation_flags_ |= VALIDATE_INITIAL_KEY; |
| set_owning_domain(owning_domain); |
| } |
| |
| void CloudPolicyValidatorBase::ValidateAgainstCurrentPolicy( |
| const em::PolicyData* policy_data, |
| ValidateTimestampOption timestamp_option, |
| ValidateDMTokenOption dm_token_option, |
| ValidateDeviceIdOption device_id_option) { |
| base::Time last_policy_timestamp; |
| std::string expected_dm_token; |
| std::string expected_device_id; |
| if (policy_data) { |
| last_policy_timestamp = base::Time::FromJavaTime(policy_data->timestamp()); |
| expected_dm_token = policy_data->request_token(); |
| expected_device_id = policy_data->device_id(); |
| } |
| ValidateTimestamp(last_policy_timestamp, timestamp_option); |
| ValidateDMToken(expected_dm_token, dm_token_option); |
| ValidateDeviceId(expected_device_id, device_id_option); |
| } |
| |
| CloudPolicyValidatorBase::CloudPolicyValidatorBase( |
| std::unique_ptr<em::PolicyFetchResponse> policy_response, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner) |
| : validation_flags_(0), |
| status_(VALIDATION_OK), |
| policy_(std::move(policy_response)), |
| timestamp_not_before_(0), |
| timestamp_option_(TIMESTAMP_VALIDATED), |
| dm_token_option_(DM_TOKEN_REQUIRED), |
| device_id_option_(DEVICE_ID_REQUIRED), |
| canonicalize_user_(false), |
| verification_key_(GetPolicyVerificationKey()), |
| allow_key_rotation_(false), |
| background_task_runner_(background_task_runner) { |
| DCHECK(!verification_key_.empty()); |
| } |
| |
| // static |
| void CloudPolicyValidatorBase::PostValidationTask( |
| std::unique_ptr<CloudPolicyValidatorBase> validator, |
| const base::Closure& completion_callback) { |
| const auto task_runner = validator->background_task_runner_; |
| task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CloudPolicyValidatorBase::PerformValidation, |
| std::move(validator), base::ThreadTaskRunnerHandle::Get(), |
| completion_callback)); |
| } |
| |
| // static |
| void CloudPolicyValidatorBase::PerformValidation( |
| std::unique_ptr<CloudPolicyValidatorBase> self, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
| const base::Closure& completion_callback) { |
| // Run the validation activities on this thread. |
| self->RunValidation(); |
| |
| // Report completion on |task_runner|. |
| task_runner->PostTask( |
| FROM_HERE, base::BindOnce(&CloudPolicyValidatorBase::ReportCompletion, |
| std::move(self), completion_callback)); |
| } |
| |
| // static |
| void CloudPolicyValidatorBase::ReportCompletion( |
| std::unique_ptr<CloudPolicyValidatorBase> self, |
| const base::Closure& completion_callback) { |
| completion_callback.Run(); |
| } |
| |
| void CloudPolicyValidatorBase::RunValidation() { |
| policy_data_.reset(new em::PolicyData()); |
| RunChecks(); |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckProtoPayload( |
| google::protobuf::MessageLite* payload) { |
| DCHECK(payload); |
| if (!policy_data_ || !policy_data_->has_policy_value() || |
| !payload->ParseFromString(policy_data_->policy_value()) || |
| !payload->IsInitialized()) { |
| LOG(ERROR) << "Failed to decode policy payload protobuf"; |
| return VALIDATION_POLICY_PARSE_ERROR; |
| } |
| return VALIDATION_OK; |
| } |
| |
| void CloudPolicyValidatorBase::RunChecks() { |
| status_ = VALIDATION_OK; |
| if ((policy_->has_error_code() && policy_->error_code() != 200) || |
| (policy_->has_error_message() && !policy_->error_message().empty())) { |
| LOG(ERROR) << "Error in policy blob." |
| << " code: " << policy_->error_code() |
| << " message: " << policy_->error_message(); |
| status_ = VALIDATION_ERROR_CODE_PRESENT; |
| return; |
| } |
| |
| // Parse policy data. |
| if (!policy_data_->ParseFromString(policy_->policy_data()) || |
| !policy_data_->IsInitialized()) { |
| LOG(ERROR) << "Failed to parse policy response"; |
| status_ = VALIDATION_PAYLOAD_PARSE_ERROR; |
| return; |
| } |
| |
| // Table of checks we run. These are sorted by descending severity of the |
| // error, s.t. the most severe check will determine the validation status. |
| static const struct { |
| int flag; |
| Status (CloudPolicyValidatorBase::*checkFunction)(); |
| } 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_DM_TOKEN, &CloudPolicyValidatorBase::CheckDMToken}, |
| {VALIDATE_DEVICE_ID, &CloudPolicyValidatorBase::CheckDeviceId}, |
| {VALIDATE_USER, &CloudPolicyValidatorBase::CheckUser}, |
| {VALIDATE_DOMAIN, &CloudPolicyValidatorBase::CheckDomain}, |
| {VALIDATE_TIMESTAMP, &CloudPolicyValidatorBase::CheckTimestamp}, |
| {VALIDATE_PAYLOAD, &CloudPolicyValidatorBase::CheckPayload}, |
| {VALIDATE_VALUES, &CloudPolicyValidatorBase::CheckValues}, |
| }; |
| |
| for (size_t i = 0; i < base::size(kCheckFunctions); ++i) { |
| if (validation_flags_ & kCheckFunctions[i].flag) { |
| status_ = (this->*(kCheckFunctions[i].checkFunction))(); |
| if (status_ != VALIDATION_OK) |
| break; |
| } |
| } |
| } |
| |
| // Verifies the |new_public_key_verification_signature_deprecated| for the |
| // |new_public_key| in the policy blob. |
| bool CloudPolicyValidatorBase::CheckNewPublicKeyVerificationSignature() { |
| if (!policy_->has_new_public_key_verification_signature_deprecated()) { |
| // 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); |
| return false; |
| } |
| |
| if (!CheckVerificationKeySignature( |
| policy_->new_public_key(), verification_key_, |
| policy_->new_public_key_verification_signature_deprecated())) { |
| LOG(ERROR) << "Signature verification failed"; |
| UMA_HISTOGRAM_ENUMERATION(kMetricPolicyKeyVerification, |
| METRIC_POLICY_KEY_VERIFICATION_FAILED, |
| METRIC_POLICY_KEY_VERIFICATION_SIZE); |
| return false; |
| } |
| // Signature verification succeeded - return success to the caller. |
| DVLOG(1) << "Signature verification succeeded"; |
| 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) { |
| DCHECK(!verification_key.empty()); |
| em::DEPRECATEDPolicyPublicKeyAndDomain 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_owning_domain( |
| const std::string& owning_domain) { |
| // Make sure we aren't overwriting the owning domain with a different one. |
| DCHECK(owning_domain_.empty() || owning_domain_ == owning_domain); |
| owning_domain_ = owning_domain; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckSignature() { |
| const std::string* signature_key = &key_; |
| if (policy_->has_new_public_key() && allow_key_rotation_) { |
| signature_key = &policy_->new_public_key(); |
| if (!policy_->has_new_public_key_signature() || |
| !VerifySignature(policy_->new_public_key(), key_, |
| policy_->new_public_key_signature(), SHA1)) { |
| 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() || |
| !VerifySignature(policy_->policy_data(), *signature_key, |
| policy_->policy_data_signature(), SHA1)) { |
| LOG(ERROR) << "Policy signature validation failed"; |
| return VALIDATION_BAD_SIGNATURE; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckInitialKey() { |
| if (!policy_->has_new_public_key() || !policy_->has_policy_data_signature() || |
| !VerifySignature(policy_->policy_data(), policy_->new_public_key(), |
| policy_->policy_data_signature(), SHA1)) { |
| LOG(ERROR) << "Initial policy signature validation failed"; |
| return VALIDATION_BAD_INITIAL_SIGNATURE; |
| } |
| |
| if (!CheckNewPublicKeyVerificationSignature()) { |
| LOG(ERROR) << "Initial policy root signature validation failed"; |
| return VALIDATION_BAD_KEY_VERIFICATION_SIGNATURE; |
| } |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckCachedKey() { |
| if (!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_) { |
| LOG(ERROR) << "Wrong policy type " << policy_data_->policy_type(); |
| return VALIDATION_WRONG_POLICY_TYPE; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckEntityId() { |
| if (!policy_data_->has_settings_entity_id() || |
| policy_data_->settings_entity_id() != settings_entity_id_) { |
| LOG(ERROR) << "Wrong settings_entity_id " |
| << policy_data_->settings_entity_id() << ", expected " |
| << settings_entity_id_; |
| return VALIDATION_WRONG_SETTINGS_ENTITY_ID; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckTimestamp() { |
| if (timestamp_option_ == TIMESTAMP_NOT_VALIDATED) |
| return VALIDATION_OK; |
| |
| if (!policy_data_->has_timestamp()) { |
| LOG(ERROR) << "Policy timestamp missing"; |
| return VALIDATION_BAD_TIMESTAMP; |
| } |
| |
| if (policy_data_->timestamp() < timestamp_not_before_) { |
| LOG(ERROR) << "Policy too old: " << policy_data_->timestamp(); |
| return VALIDATION_BAD_TIMESTAMP; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDMToken() { |
| if (dm_token_option_ == DM_TOKEN_REQUIRED && |
| (!policy_data_->has_request_token() || |
| policy_data_->request_token().empty())) { |
| LOG(ERROR) << "Empty DM token encountered - expected: " << dm_token_; |
| return VALIDATION_BAD_DM_TOKEN; |
| } |
| if (!dm_token_.empty() && policy_data_->request_token() != dm_token_) { |
| LOG(ERROR) << "Invalid DM token: " << policy_data_->request_token() |
| << " - expected: " << dm_token_; |
| return VALIDATION_BAD_DM_TOKEN; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDeviceId() { |
| if (device_id_option_ == DEVICE_ID_REQUIRED && |
| (!policy_data_->has_device_id() || policy_data_->device_id().empty())) { |
| LOG(ERROR) << "Empty device id encountered - expected: " << device_id_; |
| return VALIDATION_BAD_DEVICE_ID; |
| } |
| if (!device_id_.empty() && policy_data_->device_id() != device_id_) { |
| LOG(ERROR) << "Invalid device id: " << policy_data_->device_id() |
| << " - expected: " << device_id_; |
| return VALIDATION_BAD_DEVICE_ID; |
| } |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckUser() { |
| if (!policy_data_->has_username() && !policy_data_->has_gaia_id()) { |
| LOG(ERROR) << "Policy is missing user name and gaia id"; |
| return VALIDATION_BAD_USER; |
| } |
| |
| if (policy_data_->has_gaia_id() && !policy_data_->gaia_id().empty() && |
| account_id_.GetAccountType() == AccountType::GOOGLE && |
| !account_id_.GetGaiaId().empty()) { |
| std::string expected = account_id_.GetGaiaId(); |
| std::string actual = policy_data_->gaia_id(); |
| |
| if (expected != actual) { |
| LOG(ERROR) << "Invalid gaia id: " << actual; |
| UMA_HISTOGRAM_ENUMERATION(kMetricPolicyUserVerification, |
| MetricPolicyUserVerification::kGaiaIdFailed); |
| return VALIDATION_BAD_USER; |
| } |
| UMA_HISTOGRAM_ENUMERATION(kMetricPolicyUserVerification, |
| MetricPolicyUserVerification::kGaiaIdSucceeded); |
| } else { |
| std::string expected = account_id_.GetUserEmail(); |
| std::string actual = policy_data_->username(); |
| if (canonicalize_user_) { |
| expected = gaia::CanonicalizeEmail(gaia::SanitizeEmail(expected)); |
| actual = gaia::CanonicalizeEmail(gaia::SanitizeEmail(actual)); |
| } |
| |
| if (expected != actual) { |
| LOG(ERROR) << "Invalid user name " << policy_data_->username(); |
| UMA_HISTOGRAM_ENUMERATION(kMetricPolicyUserVerification, |
| MetricPolicyUserVerification::kUsernameFailed); |
| return VALIDATION_BAD_USER; |
| } |
| if (account_id_.GetAccountType() != AccountType::GOOGLE || |
| account_id_.GetGaiaId().empty()) { |
| UMA_HISTOGRAM_ENUMERATION( |
| kMetricPolicyUserVerification, |
| MetricPolicyUserVerification::kUsernameSucceeded); |
| } else { |
| UMA_HISTOGRAM_ENUMERATION( |
| kMetricPolicyUserVerification, |
| MetricPolicyUserVerification::kGaiaIdMissingUsernameSucceeded); |
| } |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| CloudPolicyValidatorBase::Status CloudPolicyValidatorBase::CheckDomain() { |
| std::string policy_domain = ExtractDomainFromPolicy(); |
| if (policy_domain.empty()) { |
| LOG(ERROR) << "Policy is missing user name"; |
| return VALIDATION_BAD_USER; |
| } |
| |
| if (domain_ != policy_domain) { |
| LOG(ERROR) << "Invalid user name " << policy_data_->username(); |
| return VALIDATION_BAD_USER; |
| } |
| |
| return VALIDATION_OK; |
| } |
| |
| // static |
| bool CloudPolicyValidatorBase::VerifySignature(const std::string& data, |
| const std::string& key, |
| const std::string& signature, |
| SignatureType signature_type) { |
| crypto::SignatureVerifier verifier; |
| crypto::SignatureVerifier::SignatureAlgorithm algorithm; |
| switch (signature_type) { |
| case SHA1: |
| algorithm = crypto::SignatureVerifier::RSA_PKCS1_SHA1; |
| break; |
| case SHA256: |
| algorithm = crypto::SignatureVerifier::RSA_PKCS1_SHA256; |
| break; |
| default: |
| NOTREACHED() << "Invalid signature type: " << signature_type; |
| return false; |
| } |
| |
| if (!verifier.VerifyInit(algorithm, |
| base::as_bytes(base::make_span(signature)), |
| base::as_bytes(base::make_span(key)))) { |
| DLOG(ERROR) << "Invalid verification signature/key format"; |
| return false; |
| } |
| verifier.VerifyUpdate(base::as_bytes(base::make_span(data))); |
| return verifier.VerifyFinal(); |
| } |
| |
| template class CloudPolicyValidator<em::CloudPolicySettings>; |
| |
| #if !defined(OS_ANDROID) && !defined(OS_IOS) |
| template class CloudPolicyValidator<em::ExternalPolicyData>; |
| #endif |
| |
| } // namespace policy |