| // 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. |
| |
| #ifndef CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_ |
| #define CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_ |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/policy/proto/cloud/chrome_extension_policy.pb.h" |
| #include "policy/proto/cloud_policy.pb.h" |
| |
| namespace base { |
| class MessageLoopProxy; |
| } |
| |
| namespace google { |
| namespace protobuf { |
| class MessageLite; |
| } |
| } |
| |
| namespace enterprise_management { |
| class PolicyData; |
| class PolicyFetchResponse; |
| } |
| |
| namespace policy { |
| |
| // Helper class that implements the gory details of validating a policy blob. |
| // Since signature checks are expensive, validation can happen on a background |
| // thread. The pattern is to create a validator, configure its behavior through |
| // the ValidateXYZ() functions, and then call StartValidation(). Alternatively, |
| // RunValidation() can be used to perform validation on the current thread. |
| class 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. |
| enum Status { |
| // Indicates successful validation. |
| VALIDATION_OK, |
| // Bad signature on the initial key. |
| VALIDATION_BAD_INITIAL_SIGNATURE, |
| // Bad signature. |
| VALIDATION_BAD_SIGNATURE, |
| // Policy blob contains error code. |
| VALIDATION_ERROR_CODE_PRESENT, |
| // Policy payload failed to decode. |
| VALIDATION_PAYLOAD_PARSE_ERROR, |
| // Unexpected policy type. |
| VALIDATION_WRONG_POLICY_TYPE, |
| // Unexpected settings entity id. |
| VALIDATION_WRONG_SETTINGS_ENTITY_ID, |
| // Time stamp from the future. |
| VALIDATION_BAD_TIMESTAMP, |
| // Token doesn't match. |
| VALIDATION_WRONG_TOKEN, |
| // Username doesn't match. |
| VALIDATION_BAD_USERNAME, |
| // Policy payload protobuf parse error. |
| VALIDATION_POLICY_PARSE_ERROR, |
| }; |
| |
| enum ValidateDMTokenOption { |
| // The policy must have a non-empty DMToken. |
| DM_TOKEN_REQUIRED, |
| |
| // The policy may have an empty or missing DMToken, if the expected token |
| // is also empty. |
| DM_TOKEN_NOT_REQUIRED, |
| }; |
| |
| enum ValidateTimestampOption { |
| // The policy must have a timestamp field and it should be checked against |
| // both the start and end times. |
| TIMESTAMP_REQUIRED, |
| |
| // The timestamp should only be compared vs the |not_before| value (this |
| // is appropriate for platforms with unreliable system times, where we want |
| // to ensure that fresh policy is newer than existing policy, but we can't |
| // do any other validation). |
| TIMESTAMP_NOT_BEFORE, |
| |
| // No timestamp field is required. |
| TIMESTAMP_NOT_REQUIRED, |
| }; |
| |
| virtual ~CloudPolicyValidatorBase(); |
| |
| // Validation status which can be read after completion has been signaled. |
| Status status() const { return status_; } |
| bool success() const { return status_ == VALIDATION_OK; } |
| |
| // The policy objects owned by the validator. These are scoped_ptr |
| // references, so ownership can be passed on once validation is complete. |
| scoped_ptr<enterprise_management::PolicyFetchResponse>& policy() { |
| return policy_; |
| } |
| scoped_ptr<enterprise_management::PolicyData>& policy_data() { |
| return policy_data_; |
| } |
| |
| // Instructs the validator to check that the policy timestamp is not before |
| // |not_before| and not after |not_after| + grace interval. If |
| // |timestamp_option| is set to TIMESTAMP_REQUIRED, then the policy will fail |
| // validation if it does not have a timestamp field. |
| void ValidateTimestamp(base::Time not_before, |
| base::Time not_after, |
| ValidateTimestampOption timestamp_option); |
| |
| // Validates the username in the policy blob matches |expected_user|. |
| void ValidateUsername(const std::string& expected_user); |
| |
| // Validates the policy blob is addressed to |expected_domain|. This uses the |
| // domain part of the username field in the policy for the check. |
| void ValidateDomain(const std::string& expected_domain); |
| |
| // Makes sure the DM token on the policy matches |expected_token|. |
| // If |dm_token_option| is DM_TOKEN_REQUIRED, then the policy will fail |
| // validation if it does not have a non-empty request_token field. |
| void ValidateDMToken(const std::string& dm_token, |
| ValidateDMTokenOption dm_token_option); |
| |
| // Validates the policy type. |
| void ValidatePolicyType(const std::string& policy_type); |
| |
| // Validates the settings_entity_id value. |
| void ValidateSettingsEntityId(const std::string& settings_entity_id); |
| |
| // Validates that the payload can be decoded successfully. |
| void ValidatePayload(); |
| |
| // 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, |
| bool allow_key_rotation); |
| |
| // Similar to StartSignatureVerification(), 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(); |
| |
| // Convenience helper that configures timestamp and token validation based on |
| // the current policy blob. |policy_data| may be NULL, in which case the |
| // timestamp validation will drop the lower bound. |dm_token_option| |
| // and |timestamp_option| have the same effect as the corresponding |
| // parameters for ValidateTimestamp() and ValidateDMToken(). |
| void ValidateAgainstCurrentPolicy( |
| const enterprise_management::PolicyData* policy_data, |
| ValidateTimestampOption timestamp_option, |
| ValidateDMTokenOption dm_token_option); |
| |
| // Immediately performs validation on the current thread. |
| void RunValidation(); |
| |
| protected: |
| // Create a new validator that checks |policy_response|. |payload| is the |
| // message that the policy payload will be parsed to, and it needs to stay |
| // valid for the lifetime of the validator. |
| CloudPolicyValidatorBase( |
| scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, |
| google::protobuf::MessageLite* payload, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner); |
| |
| // Posts an asynchronous calls to PerformValidation, which will eventually |
| // report its result via |completion_callback|. |
| void PostValidationTask(const base::Closure& completion_callback); |
| |
| private: |
| // Internal flags indicating what to check. |
| enum ValidationFlags { |
| VALIDATE_TIMESTAMP = 1 << 0, |
| VALIDATE_USERNAME = 1 << 1, |
| VALIDATE_DOMAIN = 1 << 2, |
| VALIDATE_TOKEN = 1 << 3, |
| VALIDATE_POLICY_TYPE = 1 << 4, |
| VALIDATE_ENTITY_ID = 1 << 5, |
| VALIDATE_PAYLOAD = 1 << 6, |
| VALIDATE_SIGNATURE = 1 << 7, |
| VALIDATE_INITIAL_KEY = 1 << 8, |
| }; |
| |
| // Performs validation, called on a background thread. |
| static void PerformValidation( |
| scoped_ptr<CloudPolicyValidatorBase> self, |
| scoped_refptr<base::MessageLoopProxy> message_loop, |
| const base::Closure& completion_callback); |
| |
| // Reports completion to the |completion_callback_|. |
| static void ReportCompletion(scoped_ptr<CloudPolicyValidatorBase> self, |
| const base::Closure& completion_callback); |
| |
| // Invokes all the checks and reports the result. |
| void RunChecks(); |
| |
| // Helper functions implementing individual checks. |
| Status CheckTimestamp(); |
| Status CheckUsername(); |
| Status CheckDomain(); |
| Status CheckToken(); |
| Status CheckPolicyType(); |
| Status CheckEntityId(); |
| Status CheckPayload(); |
| Status CheckSignature(); |
| Status CheckInitialKey(); |
| |
| // Verifies the SHA1/RSA |signature| on |data| against |key|. |
| static bool VerifySignature(const std::string& data, |
| const std::string& key, |
| const std::string& signature); |
| |
| Status status_; |
| scoped_ptr<enterprise_management::PolicyFetchResponse> policy_; |
| scoped_ptr<enterprise_management::PolicyData> policy_data_; |
| google::protobuf::MessageLite* payload_; |
| |
| int validation_flags_; |
| int64 timestamp_not_before_; |
| int64 timestamp_not_after_; |
| ValidateTimestampOption timestamp_option_; |
| ValidateDMTokenOption dm_token_option_; |
| std::string user_; |
| std::string domain_; |
| std::string token_; |
| std::string policy_type_; |
| std::string settings_entity_id_; |
| std::string key_; |
| bool allow_key_rotation_; |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidatorBase); |
| }; |
| |
| // A simple type-parameterized extension of CloudPolicyValidator that |
| // facilitates working with the actual protobuf payload type. |
| template<typename PayloadProto> |
| class CloudPolicyValidator : public CloudPolicyValidatorBase { |
| public: |
| typedef base::Callback<void(CloudPolicyValidator<PayloadProto>*)> |
| CompletionCallback; |
| |
| virtual ~CloudPolicyValidator() {} |
| |
| // Creates a new validator. |
| // |background_task_runner| is optional; if RunValidation() is used directly |
| // and StartValidation() is not used then it can be NULL. |
| static CloudPolicyValidator<PayloadProto>* Create( |
| scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner) { |
| return new CloudPolicyValidator( |
| policy_response.Pass(), |
| scoped_ptr<PayloadProto>(new PayloadProto()), |
| background_task_runner); |
| } |
| |
| scoped_ptr<PayloadProto>& payload() { |
| return payload_; |
| } |
| |
| // Kicks off asynchronous validation. |completion_callback| is invoked when |
| // done. From this point on, the validator manages its own lifetime - this |
| // allows callers to provide a WeakPtr in the callback without leaking the |
| // validator. |
| void StartValidation(const CompletionCallback& completion_callback) { |
| PostValidationTask(base::Bind(completion_callback, this)); |
| } |
| |
| private: |
| CloudPolicyValidator( |
| scoped_ptr<enterprise_management::PolicyFetchResponse> policy_response, |
| scoped_ptr<PayloadProto> payload, |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner) |
| : CloudPolicyValidatorBase(policy_response.Pass(), |
| payload.get(), |
| background_task_runner), |
| payload_(payload.Pass()) {} |
| |
| scoped_ptr<PayloadProto> payload_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CloudPolicyValidator); |
| }; |
| |
| typedef CloudPolicyValidator<enterprise_management::CloudPolicySettings> |
| UserCloudPolicyValidator; |
| typedef CloudPolicyValidator<enterprise_management::ExternalPolicyData> |
| ComponentCloudPolicyValidator; |
| |
| } // namespace policy |
| |
| #endif // CHROME_BROWSER_POLICY_CLOUD_CLOUD_POLICY_VALIDATOR_H_ |